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/gitlab/audit.py · raw
1"""
2GitLab audit CLI.
3
4Runs all collectors against a GitLab group and writes a timestamped audit
5package to an output directory.
6
7Usage:
8 export GITLAB_TOKEN=your_token
9 export GITLAB_GROUP=your_group_id_or_path
10
11 python audit.py
12 python audit.py --group my-group
13 python audit.py --group my-group --out ./output
14 python audit.py --group my-group --url https://gitlab.example.com/api/v4
15
16Output:
17 <out>/gitlab_audit_<group>_<date>/
18 group_members.csv
19 projects.csv
20 project_members.csv
21 branch_protections.csv
22 pipelines.csv
23 approval_rules.csv
24 audit_events.csv
25 password_policy.csv
26 summary.txt
27"""
28
29import argparse
30import os
31import sys
32from datetime import date
33
34import config
35from collectors import (
36 approvals,
37 audit_events,
38 branch_protections,
39 members,
40 pipelines,
41 projects,
42 settings,
43)
44from reporters import csv_reporter
45
46
47def parse_args():
48 parser = argparse.ArgumentParser(
49 description="Generate a GitLab audit package for a group."
50 )
51 parser.add_argument(
52 "--group",
53 help="GitLab group ID or path. Overrides GITLAB_GROUP env var.",
54 )
55 parser.add_argument(
56 "--url",
57 help="GitLab API base URL. Overrides GITLAB_URL env var. "
58 "Default: https://gitlab.com/api/v4",
59 )
60 parser.add_argument(
61 "--out",
62 default="./output",
63 help="Directory to write the audit package into. Default: ./output",
64 )
65 return parser.parse_args()
66
67
68def run():
69 args = parse_args()
70 cfg = config.load(group_override=args.group, base_url_override=args.url)
71 group = cfg["group"]
72
73 safe_group = group.replace("/", "-")
74 output_dir = os.path.join(
75 args.out, f"gitlab_audit_{safe_group}_{date.today().isoformat()}"
76 )
77
78 print(f"GitLab Audit — {group}")
79 print(f"Output directory: {output_dir}")
80 print()
81
82 sections = []
83
84 def collect(label, fn, filename, *fn_args):
85 print(f"Collecting: {label}...")
86 try:
87 rows = fn(*fn_args)
88 except Exception as e:
89 print(f" Error: {e}", file=sys.stderr)
90 rows = []
91 csv_reporter.write(output_dir, filename, rows)
92 sections.append((label, len(rows)))
93 return rows
94
95 print("Enumerating projects (shared cache)...")
96 try:
97 project_cache = projects.fetch_projects(group, cfg)
98 except Exception as e:
99 print(f" Error enumerating projects: {e}", file=sys.stderr)
100 project_cache = []
101
102 collect("Group members", members.group_members, "group_members.csv", group, cfg)
103 collect(
104 "Projects", projects.project_list, "projects.csv", group, cfg, project_cache
105 )
106 collect(
107 "Project members",
108 members.project_members,
109 "project_members.csv",
110 group,
111 cfg,
112 project_cache,
113 )
114 collect(
115 "Branch protections",
116 branch_protections.branch_protections,
117 "branch_protections.csv",
118 group,
119 cfg,
120 project_cache,
121 )
122 collect(
123 "Pipelines", pipelines.pipelines, "pipelines.csv", group, cfg, project_cache
124 )
125 collect(
126 "Approval rules",
127 approvals.approval_rules,
128 "approval_rules.csv",
129 group,
130 cfg,
131 project_cache,
132 )
133 collect("Audit events", audit_events.audit_events, "audit_events.csv", group, cfg)
134 collect(
135 "Password policy",
136 settings.password_policy,
137 "password_policy.csv",
138 group,
139 cfg,
140 )
141
142 print()
143 csv_reporter.write_summary(output_dir, group, sections)
144 print()
145 print("Done.")
146
147
148if __name__ == "__main__":
149 run()