1use libverify_core::control::evaluate_all;
14use libverify_core::controls::all_controls;
15use libverify_core::evidence::*;
16use libverify_core::profile::{GateDecision, apply_profile};
17use libverify_policy::OpaProfile;
18
19fn startup_evidence() -> EvidenceBundle {
21 let pr = GovernedChange {
23 id: ChangeRequestId::new("github", "42"),
24 title: "feat: add user auth".to_string(),
25 summary: Some("Adds JWT-based authentication flow for the API.".to_string()),
26 submitted_by: Some("alice".to_string()),
27 changed_assets: EvidenceState::complete(vec![
28 ChangedAsset {
29 path: "src/auth/mod.rs".to_string(),
30 diff_available: true,
31 additions: 120,
32 deletions: 5,
33 status: "added".to_string(),
34 diff: None,
35 },
36 ChangedAsset {
37 path: "src/auth/jwt.rs".to_string(),
38 diff_available: true,
39 additions: 85,
40 deletions: 0,
41 status: "added".to_string(),
42 diff: None,
43 },
44 ChangedAsset {
45 path: "tests/auth_test.rs".to_string(),
46 diff_available: true,
47 additions: 40,
48 deletions: 0,
49 status: "added".to_string(),
50 diff: None,
51 },
52 ]),
53 approval_decisions: EvidenceState::complete(vec![ApprovalDecision {
54 actor: "alice".to_string(), disposition: ApprovalDisposition::Approved,
56 submitted_at: Some("2026-03-25T10:00:00Z".to_string()),
57 }]),
58 source_revisions: EvidenceState::complete(vec![SourceRevision {
59 id: "abc1234".to_string(),
60 authored_by: Some("alice".to_string()),
61 committed_at: Some("2026-03-25T09:30:00Z".to_string()),
62 merge: false,
63 authenticity: EvidenceState::complete(AuthenticityEvidence {
64 verified: false, mechanism: None,
66 }),
67 }]),
68 work_item_refs: EvidenceState::complete(vec![]), };
70
71 let posture = RepositoryPosture {
73 codeowners_entries: vec![], secret_scanning_enabled: false, secret_push_protection_enabled: false, vulnerability_scanning_enabled: true, code_scanning_enabled: false, security_policy_present: true, security_policy_has_disclosure: false, default_branch_protected: false, };
82
83 EvidenceBundle {
84 change_requests: vec![pr],
85 promotion_batches: vec![],
86 artifact_attestations: EvidenceState::NotApplicable,
87 check_runs: EvidenceState::complete(vec![
88 CheckRunEvidence {
89 name: "ci / test".to_string(),
90 conclusion: CheckConclusion::Success,
91 app_slug: Some("github-actions".to_string()),
92 },
93 CheckRunEvidence {
94 name: "ci / lint".to_string(),
95 conclusion: CheckConclusion::Success,
96 app_slug: Some("github-actions".to_string()),
97 },
98 ]),
99 build_platform: EvidenceState::NotApplicable, dependency_signatures: EvidenceState::NotApplicable, repository_posture: EvidenceState::complete(posture),
102 }
103}
104
105fn main() {
106 println!("==========================================================");
107 println!(" libverify: OSS -> SOC2 Graduation Path Demo");
108 println!(" Startup: 15-person team preparing for SOC2 Type II");
109 println!("==========================================================\n");
110
111 println!("STARTUP POSTURE:");
113 println!(" - No CODEOWNERS file");
114 println!(" - Dependabot enabled, no secret scanning");
115 println!(" - Basic SECURITY.md (no disclosure process)");
116 println!(" - Self-reviewed PRs (author == sole reviewer)");
117 println!(" - Unsigned commits, no branch protection");
118 println!(" - CI running (tests + lint pass), no build provenance");
119 println!(" - No dependency signature verification");
120 println!();
121
122 let evidence = startup_evidence();
124 let controls = all_controls();
125 println!("Running {} controls against evidence...\n", controls.len());
126 let findings = evaluate_all(&controls, &evidence);
127
128 let oss_profile = OpaProfile::oss_preset().expect("OSS preset should load");
130 let oss_outcomes = apply_profile(&oss_profile, &findings);
131
132 let soc2_profile = OpaProfile::soc2_preset().expect("SOC2 preset should load");
134 let soc2_outcomes = apply_profile(&soc2_profile, &findings);
135
136 println!(
138 "{:<40} {:<12} {:<12} {}",
139 "CONTROL", "OSS", "SOC2", "DELTA"
140 );
141 println!("{}", "-".repeat(90));
142
143 let mut oss_pass = 0u32;
144 let mut oss_review = 0u32;
145 let mut oss_fail = 0u32;
146 let mut soc2_pass = 0u32;
147 let mut soc2_review = 0u32;
148 let mut soc2_fail = 0u32;
149 let mut graduation_blockers: Vec<(String, String)> = Vec::new();
150
151 for (oss_out, soc2_out) in oss_outcomes.iter().zip(soc2_outcomes.iter()) {
152 let oss_dec = oss_out.decision;
153 let soc2_dec = soc2_out.decision;
154
155 match oss_dec {
156 GateDecision::Pass => oss_pass += 1,
157 GateDecision::Review => oss_review += 1,
158 GateDecision::Fail => oss_fail += 1,
159 }
160 match soc2_dec {
161 GateDecision::Pass => soc2_pass += 1,
162 GateDecision::Review => soc2_review += 1,
163 GateDecision::Fail => soc2_fail += 1,
164 }
165
166 let delta = if oss_dec == soc2_dec {
167 " ".to_string()
168 } else {
169 let arrow = format!("{} -> {}", oss_dec, soc2_dec);
170 if soc2_dec == GateDecision::Fail {
171 graduation_blockers.push((
172 oss_out.control_id.as_str().to_string(),
173 oss_out.rationale.clone(),
174 ));
175 format!("!! {arrow}")
176 } else {
177 format!(" {arrow}")
178 }
179 };
180
181 println!(
182 "{:<40} {:<12} {:<12} {}",
183 oss_out.control_id.as_str(),
184 format!("{}", oss_dec),
185 format!("{}", soc2_dec),
186 delta,
187 );
188 }
189
190 println!("\n{}", "=".repeat(90));
192 println!("SUMMARY\n");
193 println!(
194 " OSS preset: {} pass / {} review / {} fail",
195 oss_pass, oss_review, oss_fail
196 );
197 println!(
198 " SOC2 preset: {} pass / {} review / {} fail",
199 soc2_pass, soc2_review, soc2_fail
200 );
201
202 println!("\n{}", "-".repeat(90));
203 println!(
204 "GRADUATION BLOCKERS (review in OSS, fail in SOC2): {}\n",
205 graduation_blockers.len()
206 );
207 if graduation_blockers.is_empty() {
208 println!(" None -- you are already SOC2-ready (unlikely for a startup!)");
209 } else {
210 for (id, rationale) in &graduation_blockers {
211 println!(" [BLOCK] {id}");
212 println!(" {rationale}\n");
213 }
214 }
215
216 let both_fail: Vec<_> = oss_outcomes
218 .iter()
219 .zip(soc2_outcomes.iter())
220 .filter(|(o, s)| o.decision == GateDecision::Fail && s.decision == GateDecision::Fail)
221 .map(|(o, _)| o.control_id.as_str().to_string())
222 .collect();
223
224 if !both_fail.is_empty() {
225 println!("{}", "-".repeat(90));
226 println!(
227 "FAIL IN BOTH presets (fix these regardless): {}\n",
228 both_fail.len()
229 );
230 for id in &both_fail {
231 println!(" [FAIL] {id}");
232 }
233 }
234
235 let soc2_review_list: Vec<_> = soc2_outcomes
237 .iter()
238 .filter(|o| o.decision == GateDecision::Review)
239 .map(|o| o.control_id.as_str().to_string())
240 .collect();
241
242 if !soc2_review_list.is_empty() {
243 println!("\n{}", "-".repeat(90));
244 println!(
245 "SOC2 REVIEW items (auditor will ask questions): {}\n",
246 soc2_review_list.len()
247 );
248 for id in &soc2_review_list {
249 println!(" [REVIEW] {id}");
250 }
251 }
252
253 println!("\n{}", "=".repeat(90));
254 println!("GRADUATION ROADMAP:");
255 println!(" 1. Enable branch protection + require reviews (unblocks review-independence,");
256 println!(" two-party-review, branch-protection-enforcement, branch-history-integrity)");
257 println!(" 2. Enable secret scanning + push protection (unblocks secret-scanning)");
258 println!(" 3. Add CODEOWNERS file (unblocks codeowners-coverage)");
259 println!(" 4. Sign commits (GPG/SSH) (unblocks source-authenticity)");
260 println!(" 5. Link issues to PRs (unblocks issue-linkage)");
261 println!(" 6. Add disclosure process to SECURITY.md (unblocks security-policy)");
262 println!(" 7. Set up build provenance (SLSA) (unblocks build-track controls)");
263 println!("{}", "=".repeat(90));
264}