| @@ -0,0 +1,101 @@ |
| |
1 | # Ruleset: GitLab |
| |
2 | # |
| |
3 | # Evaluated against an audit-tools GitLab evidence package |
| |
4 | # (gitlab_audit_<group>_<date>/). Note two GitLab-specific facts that shape |
| |
5 | # these rules: |
| |
6 | # |
| |
7 | # * branch_protections lists one row *per protected branch* — there is no |
| |
8 | # "protected" boolean as on GitHub. A row's existence means the branch is |
| |
9 | # protected; the checks below test how strong that protection is. |
| |
10 | # * audit-tools omits a CSV entirely when a collector returns no rows, so an |
| |
11 | # absent table reports as "not applicable", not "pass". A rule can only |
| |
12 | # speak to data that was actually collected. |
| |
13 | platform: gitlab |
| |
14 | |
| |
15 | rules: |
| |
16 | - id: gitlab.branch.no-force-push |
| |
17 | title: Protected branches disallow force push |
| |
18 | table: branch_protections |
| |
19 | severity: high |
| |
20 | controls: [SOC2:CC8.1, ISO:A.8.32, NIST:CM-6] |
| |
21 | rationale: > |
| |
22 | Allowing force push to a protected branch lets history be rewritten with |
| |
23 | no trace, defeating the change-management record the protection exists to |
| |
24 | provide. |
| |
25 | remediation: Disable "Allow force push" on protected branches. |
| |
26 | check: |
| |
27 | type: fail_rows_where |
| |
28 | when: {column: allow_force_push, op: is_true} |
| |
29 | |
| |
30 | - id: gitlab.branch.code-owner-approval |
| |
31 | title: Protected branches require code owner approval |
| |
32 | table: branch_protections |
| |
33 | severity: low |
| |
34 | controls: [SOC2:CC8.1, ISO:A.8.32] |
| |
35 | rationale: > |
| |
36 | Code owner approval routes changes to the people accountable for the |
| |
37 | affected files. It is a stricter posture than a plain review requirement |
| |
38 | and is not needed everywhere — hence low severity. |
| |
39 | remediation: Enable "Require approval from code owners" on protected branches where ownership matters. |
| |
40 | check: |
| |
41 | type: fail_rows_where |
| |
42 | when: {column: code_owner_approval_required, op: is_false} |
| |
43 | |
| |
44 | - id: gitlab.approvals.require-one |
| |
45 | title: Approval rules require at least one approval |
| |
46 | table: approval_rules |
| |
47 | severity: medium |
| |
48 | controls: [SOC2:CC8.1, ISO:A.8.32, NIST:CM-6] |
| |
49 | rationale: > |
| |
50 | An approval rule that requires zero approvals contributes nothing to |
| |
51 | segregation of duties — a change can merge with no second person signing |
| |
52 | off. |
| |
53 | remediation: Set the required number of approvals to at least one on merge-blocking rules. |
| |
54 | check: |
| |
55 | type: fail_rows_where |
| |
56 | when: {column: approvals_required, op: lt, value: 1} |
| |
57 | |
| |
58 | - id: gitlab.projects.no-public |
| |
59 | title: Projects are not publicly visible |
| |
60 | table: projects |
| |
61 | severity: medium |
| |
62 | controls: [SOC2:CC6.3, ISO:A.5.15, NIST:AC-6] |
| |
63 | rationale: > |
| |
64 | A public project exposes its source and history to anyone on the |
| |
65 | internet. That is sometimes intended (open source) but should be a |
| |
66 | deliberate, reviewed decision rather than a default. |
| |
67 | remediation: Set project visibility to private or internal unless public exposure is intended and approved. |
| |
68 | check: |
| |
69 | type: fail_rows_where |
| |
70 | when: {column: visibility, op: equals, value: public} |
| |
71 | |
| |
72 | - id: gitlab.password-policy |
| |
73 | title: Instance password policy meets baseline strength |
| |
74 | table: password_policy |
| |
75 | severity: medium |
| |
76 | controls: [ISO:A.5.17, NIST:IA-5] |
| |
77 | rationale: > |
| |
78 | A weak password policy undermines every password-based control. This table |
| |
79 | is only present for self-hosted instances audited with an admin token; on |
| |
80 | GitLab.com it is absent and this rule reports as not applicable. |
| |
81 | remediation: Require a minimum length of 12 and mandate numbers and symbols in the instance settings. |
| |
82 | check: |
| |
83 | type: assert_row |
| |
84 | require: |
| |
85 | - {column: minimum_password_length, op: gte, value: 12} |
| |
86 | - {column: password_number_required, op: is_true} |
| |
87 | - {column: password_symbol_required, op: is_true} |
| |
88 | |
| |
89 | - id: gitlab.audit.logging-active |
| |
90 | title: Audit event logging is producing records |
| |
91 | table: audit_events |
| |
92 | severity: medium |
| |
93 | controls: [SOC2:CC7.2, ISO:A.8.15, NIST:AU-2] |
| |
94 | rationale: > |
| |
95 | The presence of audit events is direct evidence that GitLab is recording |
| |
96 | security-relevant activity. Absence here means no events were collected — |
| |
97 | reported as not applicable rather than as a pass or a failure. |
| |
98 | remediation: Confirm audit events are enabled and retained for the group and its projects. |
| |
99 | check: |
| |
100 | type: require_any_row |
| |
101 | when: {column: action, op: not_empty} |