Skip to main content

auths_sdk/workflows/
audit.rs

1//! Audit workflow for commit signing compliance analysis.
2//!
3//! Produces structured audit reports from git commit history.
4//! All I/O is abstracted behind the `GitLogProvider` port.
5
6use crate::ports::git::{CommitRecord, GitLogProvider, GitProviderError, SignatureStatus};
7use serde::{Deserialize, Serialize};
8
9/// Errors from audit workflow execution.
10#[derive(Debug, thiserror::Error)]
11pub enum AuditError {
12    /// A git provider error occurred while reading commit history.
13    #[error("git provider error: {0}")]
14    Provider(#[from] GitProviderError),
15}
16
17/// Structured audit report with commit entries and summary statistics.
18///
19/// Usage:
20/// ```ignore
21/// let report = workflow.generate_report(None, Some(100))?;
22/// println!("Total: {}, Signed: {}", report.summary.total_commits, report.summary.signed_commits);
23/// ```
24#[derive(Debug)]
25pub struct AuditReport {
26    /// All commit records in the audited range.
27    pub commits: Vec<CommitRecord>,
28    /// Aggregate statistics for the commit set.
29    pub summary: AuditSummary,
30}
31
32/// Summary statistics for an audit report.
33///
34/// `verification_failed` counts commits that carry a signing attempt (including
35/// `InvalidSignature`) but did not pass verification. This matches the CLI
36/// definition: `signed_commits - verification_passed`.
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct AuditSummary {
39    /// Total number of commits in the audited range.
40    pub total_commits: usize,
41    /// Commits with any signing attempt (including invalid signatures).
42    pub signed_commits: usize,
43    /// Commits with no signing attempt.
44    pub unsigned_commits: usize,
45    /// Commits signed with the auths workflow.
46    pub auths_signed: usize,
47    /// Commits signed with GPG.
48    pub gpg_signed: usize,
49    /// Commits signed with SSH.
50    pub ssh_signed: usize,
51    /// Signed commits whose signature verified successfully.
52    pub verification_passed: usize,
53    /// Signed commits whose signature did not verify.
54    pub verification_failed: usize,
55}
56
57/// Workflow that generates audit compliance reports from commit history.
58///
59/// Args:
60/// * `provider`: A `GitLogProvider` implementation for reading commits.
61///
62/// Usage:
63/// ```ignore
64/// let workflow = AuditWorkflow::new(&my_provider);
65/// let report = workflow.generate_report(None, Some(100))?;
66/// ```
67pub struct AuditWorkflow<'a, G: GitLogProvider> {
68    provider: &'a G,
69}
70
71impl<'a, G: GitLogProvider> AuditWorkflow<'a, G> {
72    /// Create a new `AuditWorkflow` backed by the given provider.
73    pub fn new(provider: &'a G) -> Self {
74        Self { provider }
75    }
76
77    /// Generate an audit report from the repository's commit history.
78    ///
79    /// Args:
80    /// * `range`: Optional git revision range spec.
81    /// * `limit`: Optional maximum number of commits.
82    pub fn generate_report(
83        &self,
84        range: Option<&str>,
85        limit: Option<usize>,
86    ) -> Result<AuditReport, AuditError> {
87        let commits = self.provider.walk_commits(range, limit)?;
88        let summary = summarize_commits(&commits);
89        Ok(AuditReport { commits, summary })
90    }
91}
92
93/// Compute an `AuditSummary` from a slice of commit records.
94///
95/// Args:
96/// * `commits`: The commit records to summarize.
97///
98/// Usage:
99/// ```ignore
100/// let summary = summarize_commits(&filtered_commits);
101/// ```
102pub fn summarize_commits(commits: &[CommitRecord]) -> AuditSummary {
103    let total_commits = commits.len();
104    let mut signed_commits = 0usize;
105    let mut auths_signed = 0usize;
106    let mut gpg_signed = 0usize;
107    let mut ssh_signed = 0usize;
108    let mut verification_passed = 0usize;
109
110    for c in commits {
111        match &c.signature_status {
112            SignatureStatus::AuthsSigned { .. } => {
113                signed_commits += 1;
114                auths_signed += 1;
115                verification_passed += 1;
116            }
117            SignatureStatus::SshSigned => {
118                signed_commits += 1;
119                ssh_signed += 1;
120            }
121            SignatureStatus::GpgSigned { verified } => {
122                signed_commits += 1;
123                gpg_signed += 1;
124                if *verified {
125                    verification_passed += 1;
126                }
127            }
128            SignatureStatus::InvalidSignature { .. } => {
129                signed_commits += 1;
130            }
131            SignatureStatus::Unsigned => {}
132        }
133    }
134
135    AuditSummary {
136        total_commits,
137        unsigned_commits: total_commits - signed_commits,
138        verification_failed: signed_commits - verification_passed,
139        signed_commits,
140        auths_signed,
141        gpg_signed,
142        ssh_signed,
143        verification_passed,
144    }
145}