libverify_core/controls/
dependency_update_tool.rs1use crate::control::{Control, ControlFinding, ControlId, builtin};
2use crate::evidence::EvidenceBundle;
3
4pub struct DependencyUpdateToolControl;
12
13impl Control for DependencyUpdateToolControl {
14 fn id(&self) -> ControlId {
15 builtin::id(builtin::DEPENDENCY_UPDATE_TOOL)
16 }
17
18 fn description(&self) -> &'static str {
19 "A dependency update tool (Dependabot or Renovate) must be configured"
20 }
21
22 fn evaluate(&self, evidence: &EvidenceBundle) -> Vec<ControlFinding> {
23 let posture = match ControlFinding::extract_posture(self.id(), evidence) {
24 Ok(p) => p,
25 Err(findings) => return findings,
26 };
27
28 if posture.dependency_update_tool_configured {
29 vec![ControlFinding::satisfied(
30 self.id(),
31 "Dependency update tool is configured (Dependabot or Renovate)",
32 vec!["repository:dependency-update-tool".into()],
33 )]
34 } else {
35 vec![ControlFinding::violated(
36 self.id(),
37 "No dependency update tool detected — configure .github/dependabot.yml or renovate.json",
38 vec!["repository:dependency-update-tool".into()],
39 )]
40 }
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47 use crate::control::ControlStatus;
48 use crate::evidence::{EvidenceState, RepositoryPosture};
49
50 fn bundle_with(configured: bool) -> EvidenceBundle {
51 EvidenceBundle {
52 repository_posture: EvidenceState::complete(RepositoryPosture {
53 dependency_update_tool_configured: configured,
54 ..Default::default()
55 }),
56 ..Default::default()
57 }
58 }
59
60 #[test]
61 fn satisfied_when_configured() {
62 let findings = DependencyUpdateToolControl.evaluate(&bundle_with(true));
63 assert_eq!(findings[0].status, ControlStatus::Satisfied);
64 }
65
66 #[test]
67 fn violated_when_not_configured() {
68 let findings = DependencyUpdateToolControl.evaluate(&bundle_with(false));
69 assert_eq!(findings[0].status, ControlStatus::Violated);
70 assert!(findings[0].rationale.contains("dependabot.yml"));
71 }
72
73 #[test]
74 fn indeterminate_when_posture_missing() {
75 let findings = DependencyUpdateToolControl.evaluate(&EvidenceBundle {
76 repository_posture: EvidenceState::missing(vec![]),
77 ..Default::default()
78 });
79 assert_eq!(findings[0].status, ControlStatus::Indeterminate);
80 }
81
82 #[test]
83 fn not_applicable_when_posture_not_applicable() {
84 let findings = DependencyUpdateToolControl.evaluate(&EvidenceBundle {
85 repository_posture: EvidenceState::not_applicable(),
86 ..Default::default()
87 });
88 assert_eq!(findings[0].status, ControlStatus::NotApplicable);
89 }
90}