Skip to main content

OpaProfile

Struct OpaProfile 

Source
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

Source

pub fn from_file(path: &str) -> Result<Self>

Loads a custom Rego policy from the given file path.

Source

pub fn from_rego(name: &str, rego: &str) -> Result<Self>

Loads a Rego policy from a string.

Source

pub fn default_policy() -> Result<Self>

Source

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
Hide additional 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}
Source

pub fn aiops_preset() -> Result<Self>

Source

pub fn soc1_preset() -> Result<Self>

Source

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
Hide additional 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}
Source

pub fn slsa_l1_preset() -> Result<Self>

Source

pub fn slsa_l2_preset() -> Result<Self>

Source

pub fn slsa_l3_preset() -> Result<Self>

Source

pub fn slsa_l4_preset() -> Result<Self>

Source

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§

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V