pub struct OpaProfile { /* private fields */ }Expand description
OPA-based profile that evaluates Rego policies to map control findings to gate decisions, enabling per-organization customization.
Implementations§
Source§impl OpaProfile
impl OpaProfile
Sourcepub fn from_file(path: &str) -> Result<Self>
pub fn from_file(path: &str) -> Result<Self>
Loads a custom Rego policy from the given file path.
pub fn default_policy() -> Result<Self>
Sourcepub fn oss_preset() -> Result<Self>
pub fn oss_preset() -> Result<Self>
Examples found in repository?
examples/audit_scenarios.rs (line 92)
91fn main() {
92 let oss = OpaProfile::oss_preset().expect("oss preset");
93 let soc2 = OpaProfile::soc2_preset().expect("soc2 preset");
94
95 let secret_ctrl = SecretScanningControl;
96 let codeowners_ctrl = CodeownersCoverageControl;
97 let vuln_ctrl = VulnerabilityScanningControl;
98 let secpol_ctrl = SecurityPolicyControl;
99
100 // --- Scenario A: secret scanning ON, push protection OFF ---
101 // Expected: Satisfied with "detection" tier (not "prevention")
102 let mut posture_a = default_posture();
103 posture_a.secret_scanning_enabled = true;
104 posture_a.secret_push_protection_enabled = false;
105 let bundle_a = bundle_from_posture(posture_a);
106
107 // --- Scenario B: CODEOWNERS with exactly 2 entries (below threshold of 3) ---
108 // Expected: Violated (insufficient targeted coverage)
109 let mut posture_b = default_posture();
110 posture_b.codeowners_entries = vec![
111 entry("/src/auth/", &["@org/security"]),
112 entry("/infra/", &["@org/platform"]),
113 ];
114 let bundle_b = bundle_from_posture(posture_b);
115
116 // --- Scenario C: CODEOWNERS with exactly 3 targeted entries (at threshold) ---
117 // Expected: Satisfied (intentional targeted coverage)
118 let mut posture_c = default_posture();
119 posture_c.codeowners_entries = vec![
120 entry("/src/auth/", &["@org/security"]),
121 entry("/infra/", &["@org/platform"]),
122 entry("/.github/", &["@org/devops"]),
123 ];
124 let bundle_c = bundle_from_posture(posture_c);
125
126 // --- Scenario D: vulnerability scanning ON, code scanning OFF ---
127 // Expected: Satisfied with "sca-only" tier (not "sca+sast")
128 let mut posture_d = default_posture();
129 posture_d.vulnerability_scanning_enabled = true;
130 posture_d.code_scanning_enabled = false;
131 let bundle_d = bundle_from_posture(posture_d);
132
133 // --- Scenario E: SECURITY.md present but no disclosure process ---
134 // Expected: Violated (policy exists but incomplete)
135 let mut posture_e = default_posture();
136 posture_e.security_policy_present = true;
137 posture_e.security_policy_has_disclosure = false;
138 let bundle_e = bundle_from_posture(posture_e);
139
140 let results = vec![
141 run_scenario(
142 "A: Secret scanning ON, push protection OFF",
143 &secret_ctrl,
144 &bundle_a,
145 ControlStatus::Satisfied,
146 &oss,
147 &soc2,
148 ),
149 run_scenario(
150 "B: CODEOWNERS 2 entries (below threshold)",
151 &codeowners_ctrl,
152 &bundle_b,
153 ControlStatus::Violated,
154 &oss,
155 &soc2,
156 ),
157 run_scenario(
158 "C: CODEOWNERS 3 entries (at threshold)",
159 &codeowners_ctrl,
160 &bundle_c,
161 ControlStatus::Satisfied,
162 &oss,
163 &soc2,
164 ),
165 run_scenario(
166 "D: Vuln scanning ON, code scanning OFF",
167 &vuln_ctrl,
168 &bundle_d,
169 ControlStatus::Satisfied,
170 &oss,
171 &soc2,
172 ),
173 run_scenario(
174 "E: SECURITY.md present, no disclosure",
175 &secpol_ctrl,
176 &bundle_e,
177 ControlStatus::Violated,
178 &oss,
179 &soc2,
180 ),
181 ];
182
183 // --- Report ---
184 println!("=== SOC2 Type II Audit Edge-Case Scenarios ===\n");
185
186 let mut all_pass = true;
187 for r in &results {
188 let status_ok = r.actual_status == r.expected_status;
189 let marker = if status_ok { "PASS" } else { "FAIL" };
190 if !status_ok {
191 all_pass = false;
192 }
193
194 println!("--- {} ---", r.name);
195 println!(
196 " Status: {} (expected: {}) [{}]",
197 r.actual_status, r.expected_status, marker
198 );
199 println!(" Rationale: {}", r.rationale);
200 println!(" Subjects: {:?}", r.subjects);
201 println!(
202 " OSS decision: {:?} SOC2 decision: {:?}",
203 r.oss_decision, r.soc2_decision
204 );
205 println!();
206 }
207
208 // --- Policy differentiation summary ---
209 println!("=== Policy Differentiation Summary ===\n");
210 for r in &results {
211 let diff = if r.oss_decision != r.soc2_decision {
212 format!(
213 "DIFFER (OSS={:?}, SOC2={:?})",
214 r.oss_decision, r.soc2_decision
215 )
216 } else {
217 format!("SAME ({:?})", r.oss_decision)
218 };
219 println!(" {}: {}", r.name, diff);
220 }
221
222 // --- Tiered subject verification ---
223 println!("\n=== Tiered Subject Verification ===\n");
224 let tier_checks: &[(&str, &str, usize)] = &[
225 ("A: detection tier", "detection", 0),
226 ("D: sca-only tier", "sca-only", 3),
227 ];
228 for (label, needle, idx) in tier_checks {
229 let found = results[*idx].subjects.iter().any(|s| s.contains(needle));
230 let mark = if found { "PRESENT" } else { "MISSING" };
231 println!(" {}: '{}' -> [{}]", label, needle, mark);
232 }
233
234 println!();
235 if all_pass {
236 println!("All scenarios produced the expected control status.");
237 } else {
238 println!("WARNING: Some scenarios did NOT match expected status.");
239 std::process::exit(1);
240 }
241}More examples
examples/startup_graduation.rs (line 129)
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 // --- Describe the startup posture ---
112 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 // --- Run all 28 controls ---
123 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 // --- Evaluate through OSS preset ---
129 let oss_profile = OpaProfile::oss_preset().expect("OSS preset should load");
130 let oss_outcomes = apply_profile(&oss_profile, &findings);
131
132 // --- Evaluate through SOC2 preset ---
133 let soc2_profile = OpaProfile::soc2_preset().expect("SOC2 preset should load");
134 let soc2_outcomes = apply_profile(&soc2_profile, &findings);
135
136 // --- Print side-by-side comparison ---
137 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 // --- Summary ---
191 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 // Also show controls that fail in both (already broken)
217 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 // Controls that are review in SOC2 (needs attention but not blocking)
236 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}pub fn aiops_preset() -> Result<Self>
pub fn soc1_preset() -> Result<Self>
Sourcepub fn soc2_preset() -> Result<Self>
pub fn soc2_preset() -> Result<Self>
Examples found in repository?
examples/fleet_assessment.rs (line 233)
232fn main() {
233 let soc2 = OpaProfile::soc2_preset().expect("SOC2 preset should load");
234 let profiles = repo_profiles();
235
236 let mut assessments: Vec<RepoAssessment> =
237 profiles.iter().map(|p| assess_repo(p, &soc2)).collect();
238
239 // Sort by risk score descending (worst first)
240 assessments.sort_by(|a, b| risk_score(b).cmp(&risk_score(a)));
241
242 // ---- Fleet Summary Table ----
243 println!("==========================================================================");
244 println!(" FLEET-WIDE ASPM ASSESSMENT — SOC2 Preset (4 ASPM Controls x 5 Repos)");
245 println!("==========================================================================");
246 println!();
247 println!(
248 " {:<20} {:>4} {:>6} {:>4} {:>5} {:<10}",
249 "Repository", "Pass", "Review", "Fail", "Score", "Risk Tier"
250 );
251 println!(
252 " {:-<20} {:->4} {:->6} {:->4} {:->5} {:-<10}",
253 "", "", "", "", "", ""
254 );
255
256 for a in &assessments {
257 let score = risk_score(a);
258 println!(
259 " {:<20} {:>4} {:>6} {:>4} {:>5} {:<10}",
260 a.name,
261 a.pass,
262 a.review,
263 a.fail,
264 score,
265 risk_tier(score),
266 );
267 }
268
269 // ---- Fleet Totals ----
270 let total_pass: u32 = assessments.iter().map(|a| a.pass).sum();
271 let total_review: u32 = assessments.iter().map(|a| a.review).sum();
272 let total_fail: u32 = assessments.iter().map(|a| a.fail).sum();
273 let total = total_pass + total_review + total_fail;
274
275 println!();
276 println!(
277 " Fleet totals: {} pass / {} review / {} fail out of {} checks",
278 total_pass, total_review, total_fail, total
279 );
280 println!(
281 " Fleet pass rate: {:.0}%",
282 if total > 0 {
283 (total_pass as f64 / total as f64) * 100.0
284 } else {
285 0.0
286 }
287 );
288
289 // ---- Per-Repo Detail ----
290 println!();
291 println!("==========================================================================");
292 println!(" PER-REPO FINDINGS (ordered by risk, worst first)");
293 println!("==========================================================================");
294
295 for a in &assessments {
296 let score = risk_score(a);
297 println!();
298 println!(
299 " [{:<10}] {} — {}",
300 risk_tier(score),
301 a.name,
302 a.description
303 );
304
305 for (control_id, decision, rationale) in &a.details {
306 let symbol = decision_symbol(*decision);
307 println!(" {:<6} {:<26} {}", symbol, control_id, rationale);
308 }
309 }
310
311 // ---- Actionable Remediation Priorities ----
312 println!();
313 println!("==========================================================================");
314 println!(" REMEDIATION PRIORITIES");
315 println!("==========================================================================");
316 println!();
317
318 // Collect all failures across fleet, grouped by control
319 let control_ids = [
320 builtin::CODEOWNERS_COVERAGE,
321 builtin::SECRET_SCANNING,
322 builtin::VULNERABILITY_SCANNING,
323 builtin::SECURITY_POLICY,
324 ];
325
326 for cid in &control_ids {
327 let failing_repos: Vec<&str> = assessments
328 .iter()
329 .filter(|a| {
330 a.details
331 .iter()
332 .any(|(id, d, _)| id == *cid && *d == GateDecision::Fail)
333 })
334 .map(|a| a.name)
335 .collect();
336
337 let review_repos: Vec<&str> = assessments
338 .iter()
339 .filter(|a| {
340 a.details
341 .iter()
342 .any(|(id, d, _)| id == *cid && *d == GateDecision::Review)
343 })
344 .map(|a| a.name)
345 .collect();
346
347 if !failing_repos.is_empty() || !review_repos.is_empty() {
348 println!(" {}", cid);
349 if !failing_repos.is_empty() {
350 println!(
351 " FAIL ({} repos): {}",
352 failing_repos.len(),
353 failing_repos.join(", ")
354 );
355 }
356 if !review_repos.is_empty() {
357 println!(
358 " REVIEW ({} repos): {}",
359 review_repos.len(),
360 review_repos.join(", ")
361 );
362 }
363 println!();
364 }
365 }
366
367 // ---- Worst Repo Call-out ----
368 if let Some(worst) = assessments.first() {
369 let score = risk_score(worst);
370 println!(
371 " Worst-posture repo: {} (score={}, tier={})",
372 worst.name,
373 score,
374 risk_tier(score)
375 );
376 println!(" Recommended: prioritize enabling secret scanning and CODEOWNERS");
377 println!(" for repos in CRITICAL/HIGH tiers before next SOC2 audit window.");
378 }
379
380 println!();
381}More examples
examples/audit_scenarios.rs (line 93)
91fn main() {
92 let oss = OpaProfile::oss_preset().expect("oss preset");
93 let soc2 = OpaProfile::soc2_preset().expect("soc2 preset");
94
95 let secret_ctrl = SecretScanningControl;
96 let codeowners_ctrl = CodeownersCoverageControl;
97 let vuln_ctrl = VulnerabilityScanningControl;
98 let secpol_ctrl = SecurityPolicyControl;
99
100 // --- Scenario A: secret scanning ON, push protection OFF ---
101 // Expected: Satisfied with "detection" tier (not "prevention")
102 let mut posture_a = default_posture();
103 posture_a.secret_scanning_enabled = true;
104 posture_a.secret_push_protection_enabled = false;
105 let bundle_a = bundle_from_posture(posture_a);
106
107 // --- Scenario B: CODEOWNERS with exactly 2 entries (below threshold of 3) ---
108 // Expected: Violated (insufficient targeted coverage)
109 let mut posture_b = default_posture();
110 posture_b.codeowners_entries = vec![
111 entry("/src/auth/", &["@org/security"]),
112 entry("/infra/", &["@org/platform"]),
113 ];
114 let bundle_b = bundle_from_posture(posture_b);
115
116 // --- Scenario C: CODEOWNERS with exactly 3 targeted entries (at threshold) ---
117 // Expected: Satisfied (intentional targeted coverage)
118 let mut posture_c = default_posture();
119 posture_c.codeowners_entries = vec![
120 entry("/src/auth/", &["@org/security"]),
121 entry("/infra/", &["@org/platform"]),
122 entry("/.github/", &["@org/devops"]),
123 ];
124 let bundle_c = bundle_from_posture(posture_c);
125
126 // --- Scenario D: vulnerability scanning ON, code scanning OFF ---
127 // Expected: Satisfied with "sca-only" tier (not "sca+sast")
128 let mut posture_d = default_posture();
129 posture_d.vulnerability_scanning_enabled = true;
130 posture_d.code_scanning_enabled = false;
131 let bundle_d = bundle_from_posture(posture_d);
132
133 // --- Scenario E: SECURITY.md present but no disclosure process ---
134 // Expected: Violated (policy exists but incomplete)
135 let mut posture_e = default_posture();
136 posture_e.security_policy_present = true;
137 posture_e.security_policy_has_disclosure = false;
138 let bundle_e = bundle_from_posture(posture_e);
139
140 let results = vec![
141 run_scenario(
142 "A: Secret scanning ON, push protection OFF",
143 &secret_ctrl,
144 &bundle_a,
145 ControlStatus::Satisfied,
146 &oss,
147 &soc2,
148 ),
149 run_scenario(
150 "B: CODEOWNERS 2 entries (below threshold)",
151 &codeowners_ctrl,
152 &bundle_b,
153 ControlStatus::Violated,
154 &oss,
155 &soc2,
156 ),
157 run_scenario(
158 "C: CODEOWNERS 3 entries (at threshold)",
159 &codeowners_ctrl,
160 &bundle_c,
161 ControlStatus::Satisfied,
162 &oss,
163 &soc2,
164 ),
165 run_scenario(
166 "D: Vuln scanning ON, code scanning OFF",
167 &vuln_ctrl,
168 &bundle_d,
169 ControlStatus::Satisfied,
170 &oss,
171 &soc2,
172 ),
173 run_scenario(
174 "E: SECURITY.md present, no disclosure",
175 &secpol_ctrl,
176 &bundle_e,
177 ControlStatus::Violated,
178 &oss,
179 &soc2,
180 ),
181 ];
182
183 // --- Report ---
184 println!("=== SOC2 Type II Audit Edge-Case Scenarios ===\n");
185
186 let mut all_pass = true;
187 for r in &results {
188 let status_ok = r.actual_status == r.expected_status;
189 let marker = if status_ok { "PASS" } else { "FAIL" };
190 if !status_ok {
191 all_pass = false;
192 }
193
194 println!("--- {} ---", r.name);
195 println!(
196 " Status: {} (expected: {}) [{}]",
197 r.actual_status, r.expected_status, marker
198 );
199 println!(" Rationale: {}", r.rationale);
200 println!(" Subjects: {:?}", r.subjects);
201 println!(
202 " OSS decision: {:?} SOC2 decision: {:?}",
203 r.oss_decision, r.soc2_decision
204 );
205 println!();
206 }
207
208 // --- Policy differentiation summary ---
209 println!("=== Policy Differentiation Summary ===\n");
210 for r in &results {
211 let diff = if r.oss_decision != r.soc2_decision {
212 format!(
213 "DIFFER (OSS={:?}, SOC2={:?})",
214 r.oss_decision, r.soc2_decision
215 )
216 } else {
217 format!("SAME ({:?})", r.oss_decision)
218 };
219 println!(" {}: {}", r.name, diff);
220 }
221
222 // --- Tiered subject verification ---
223 println!("\n=== Tiered Subject Verification ===\n");
224 let tier_checks: &[(&str, &str, usize)] = &[
225 ("A: detection tier", "detection", 0),
226 ("D: sca-only tier", "sca-only", 3),
227 ];
228 for (label, needle, idx) in tier_checks {
229 let found = results[*idx].subjects.iter().any(|s| s.contains(needle));
230 let mark = if found { "PRESENT" } else { "MISSING" };
231 println!(" {}: '{}' -> [{}]", label, needle, mark);
232 }
233
234 println!();
235 if all_pass {
236 println!("All scenarios produced the expected control status.");
237 } else {
238 println!("WARNING: Some scenarios did NOT match expected status.");
239 std::process::exit(1);
240 }
241}examples/startup_graduation.rs (line 133)
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 // --- Describe the startup posture ---
112 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 // --- Run all 28 controls ---
123 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 // --- Evaluate through OSS preset ---
129 let oss_profile = OpaProfile::oss_preset().expect("OSS preset should load");
130 let oss_outcomes = apply_profile(&oss_profile, &findings);
131
132 // --- Evaluate through SOC2 preset ---
133 let soc2_profile = OpaProfile::soc2_preset().expect("SOC2 preset should load");
134 let soc2_outcomes = apply_profile(&soc2_profile, &findings);
135
136 // --- Print side-by-side comparison ---
137 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 // --- Summary ---
191 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 // Also show controls that fail in both (already broken)
217 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 // Controls that are review in SOC2 (needs attention but not blocking)
236 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}pub fn slsa_l1_preset() -> Result<Self>
pub fn slsa_l2_preset() -> Result<Self>
pub fn slsa_l3_preset() -> Result<Self>
pub fn slsa_l4_preset() -> Result<Self>
Sourcepub fn from_preset_or_file(name: &str) -> Result<Self>
pub fn from_preset_or_file(name: &str) -> Result<Self>
Loads a built-in preset by name, or falls back to file path.
Trait Implementations§
Source§impl ControlProfile for OpaProfile
impl ControlProfile for OpaProfile
fn name(&self) -> &str
fn map(&self, finding: &ControlFinding) -> ProfileOutcome
fn severity_labels(&self) -> SeverityLabels
Auto Trait Implementations§
impl Freeze for OpaProfile
impl !RefUnwindSafe for OpaProfile
impl Send for OpaProfile
impl Sync for OpaProfile
impl Unpin for OpaProfile
impl UnsafeUnpin for OpaProfile
impl !UnwindSafe for OpaProfile
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more