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}