Skip to main content

libverify_core/controls/
default_branch_settings_baseline.rs

1use crate::control::{Control, ControlFinding, ControlId, builtin};
2use crate::evidence::EvidenceBundle;
3
4/// Validates that the default branch has a minimum security baseline.
5///
6/// Maps to NIST 800-53 CM-2 (Baseline Configuration).
7///
8/// Requires ALL of the following to be satisfied:
9/// - Branch protection is enabled on the default branch
10/// - Admin enforcement is enabled (no bypass for admins)
11/// - Stale reviews are dismissed on new push
12///
13/// This is a composite control that verifies multiple branch protection
14/// settings together, providing a holistic "baseline" check rather than
15/// individual setting checks.
16pub struct DefaultBranchSettingsBaselineControl;
17
18impl Control for DefaultBranchSettingsBaselineControl {
19    fn id(&self) -> ControlId {
20        builtin::id(builtin::DEFAULT_BRANCH_SETTINGS_BASELINE)
21    }
22
23    fn description(&self) -> &'static str {
24        "Default branch must have protection baseline: protection enabled, admin enforcement, stale review dismissal"
25    }
26
27    fn evaluate(&self, evidence: &EvidenceBundle) -> Vec<ControlFinding> {
28        let posture = match ControlFinding::extract_posture(self.id(), evidence) {
29            Ok(p) => p,
30            Err(findings) => return findings,
31        };
32
33        let mut missing = Vec::new();
34
35        if !posture.default_branch_protected {
36            missing.push("branch protection not enabled");
37        }
38        if !posture.enforce_admins {
39            missing.push("admin enforcement not enabled");
40        }
41        if !posture.dismiss_stale_reviews {
42            missing.push("stale review dismissal not enabled");
43        }
44
45        if missing.is_empty() {
46            vec![ControlFinding::satisfied(
47                self.id(),
48                "Default branch meets security baseline: protection, admin enforcement, stale review dismissal all enabled",
49                vec!["repository:branch-protection:baseline".into()],
50            )]
51        } else {
52            vec![ControlFinding::violated(
53                self.id(),
54                format!(
55                    "Default branch does not meet security baseline: {}",
56                    missing.join(", ")
57                ),
58                vec!["repository:branch-protection:baseline".into()],
59            )]
60        }
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use crate::control::ControlStatus;
68    use crate::evidence::{EvidenceState, RepositoryPosture};
69
70    fn bundle_with(protected: bool, enforce: bool, dismiss: bool) -> EvidenceBundle {
71        EvidenceBundle {
72            repository_posture: EvidenceState::complete(RepositoryPosture {
73                default_branch_protected: protected,
74                enforce_admins: enforce,
75                dismiss_stale_reviews: dismiss,
76                ..Default::default()
77            }),
78            ..Default::default()
79        }
80    }
81
82    #[test]
83    fn satisfied_when_all_enabled() {
84        let findings =
85            DefaultBranchSettingsBaselineControl.evaluate(&bundle_with(true, true, true));
86        assert_eq!(findings[0].status, ControlStatus::Satisfied);
87    }
88
89    #[test]
90    fn violated_when_no_protection() {
91        let findings =
92            DefaultBranchSettingsBaselineControl.evaluate(&bundle_with(false, false, false));
93        assert_eq!(findings[0].status, ControlStatus::Violated);
94        assert!(
95            findings[0]
96                .rationale
97                .contains("branch protection not enabled")
98        );
99    }
100
101    #[test]
102    fn violated_when_no_admin_enforcement() {
103        let findings =
104            DefaultBranchSettingsBaselineControl.evaluate(&bundle_with(true, false, true));
105        assert_eq!(findings[0].status, ControlStatus::Violated);
106        assert!(findings[0].rationale.contains("admin enforcement"));
107    }
108
109    #[test]
110    fn violated_when_no_stale_dismissal() {
111        let findings =
112            DefaultBranchSettingsBaselineControl.evaluate(&bundle_with(true, true, false));
113        assert_eq!(findings[0].status, ControlStatus::Violated);
114        assert!(findings[0].rationale.contains("stale review dismissal"));
115    }
116
117    #[test]
118    fn violated_lists_all_missing() {
119        let findings =
120            DefaultBranchSettingsBaselineControl.evaluate(&bundle_with(false, false, false));
121        assert_eq!(findings[0].status, ControlStatus::Violated);
122        assert!(
123            findings[0]
124                .rationale
125                .contains("branch protection not enabled")
126        );
127        assert!(findings[0].rationale.contains("admin enforcement"));
128        assert!(findings[0].rationale.contains("stale review dismissal"));
129    }
130
131    #[test]
132    fn indeterminate_when_posture_missing() {
133        let findings = DefaultBranchSettingsBaselineControl.evaluate(&EvidenceBundle {
134            repository_posture: EvidenceState::missing(vec![]),
135            ..Default::default()
136        });
137        assert_eq!(findings[0].status, ControlStatus::Indeterminate);
138    }
139}