pub mod audit;
pub mod config_init;
pub mod fetch;
pub mod login;
pub mod logout;
pub mod policy;
pub mod risk_accept;
pub mod scan;
pub mod status;
pub mod verdict;
pub mod wrap;
use cleanlib_client::types;
fn normalize_decision(raw: &str) -> &'static str {
match raw {
"ALLOW" | "ALLOWED_NO_FINDINGS" => "ALLOW",
"DENY" | "VECTOR_VERDICT" | "DM_THRESHOLD_BLOCK" => "DENY",
"RISK_ACCEPTANCE_REQUIRED" => "RISK_ACCEPTANCE_REQUIRED",
_ => "WARN",
}
}
pub fn scan_exit_code(decisions: &[types::PolicyDecision]) -> i32 {
let mut has_warn = false;
let mut has_risk = false;
for d in decisions {
match normalize_decision(&d.decision) {
"DENY" => return 1,
"WARN" => has_warn = true,
"RISK_ACCEPTANCE_REQUIRED" => has_risk = true,
_ => {} }
}
if has_warn {
2
} else if has_risk {
3
} else {
0
}
}
pub fn verdict_exit_code(v: &types::Verdict) -> i32 {
let raw = v.decision.as_deref().unwrap_or(v.verdict.as_str());
match normalize_decision(raw) {
"DENY" => 1,
"WARN" => 2,
"RISK_ACCEPTANCE_REQUIRED" => 3,
_ => 0,
}
}
#[cfg(test)]
mod tests {
use super::*;
use cleanlib_client::types::{PolicyDecision, Verdict};
fn d(decision: &str) -> PolicyDecision {
PolicyDecision {
decision: decision.to_string(),
..PolicyDecision::default()
}
}
fn v_with_decision(decision: &str) -> Verdict {
Verdict {
decision: Some(decision.to_string()),
..Verdict::default()
}
}
fn v_with_label(label: &str) -> Verdict {
Verdict {
verdict: label.to_string(),
decision: None,
..Verdict::default()
}
}
#[test]
fn exit_zero_when_all_allow() {
assert_eq!(scan_exit_code(&[d("ALLOW"), d("ALLOW")]), 0);
}
#[test]
fn exit_one_on_any_deny() {
assert_eq!(scan_exit_code(&[d("ALLOW"), d("DENY"), d("WARN")]), 1);
}
#[test]
fn exit_two_on_warn_no_deny() {
assert_eq!(scan_exit_code(&[d("ALLOW"), d("WARN")]), 2);
}
#[test]
fn exit_three_on_risk_only() {
assert_eq!(scan_exit_code(&[d("ALLOW"), d("RISK_ACCEPTANCE_REQUIRED")]), 3);
}
#[test]
fn deny_dominates_risk() {
assert_eq!(scan_exit_code(&[d("RISK_ACCEPTANCE_REQUIRED"), d("DENY")]), 1);
}
#[test]
fn scan_exit_one_on_vector_verdict_label() {
assert_eq!(scan_exit_code(&[d("ALLOW"), d("VECTOR_VERDICT")]), 1);
}
#[test]
fn scan_exit_one_on_dm_threshold_block_label() {
assert_eq!(scan_exit_code(&[d("DM_THRESHOLD_BLOCK")]), 1);
}
#[test]
fn scan_exit_two_on_unknown_label_fail_loud() {
assert_eq!(scan_exit_code(&[d("ALLOW"), d("SOME_NEW_LABEL")]), 2);
}
#[test]
fn scan_exit_zero_on_allowed_no_findings_label() {
assert_eq!(scan_exit_code(&[d("ALLOWED_NO_FINDINGS"), d("ALLOW")]), 0);
}
#[test]
fn verdict_exit_zero_on_allow_decision() {
assert_eq!(verdict_exit_code(&v_with_decision("ALLOW")), 0);
}
#[test]
fn verdict_exit_one_on_deny_decision() {
assert_eq!(verdict_exit_code(&v_with_decision("DENY")), 1);
}
#[test]
fn verdict_exit_two_on_warn_decision() {
assert_eq!(verdict_exit_code(&v_with_decision("WARN")), 2);
}
#[test]
fn verdict_exit_three_on_risk_acceptance_required() {
assert_eq!(
verdict_exit_code(&v_with_decision("RISK_ACCEPTANCE_REQUIRED")),
3
);
}
#[test]
fn verdict_exit_one_on_vector_verdict_label_fallback() {
assert_eq!(verdict_exit_code(&v_with_label("VECTOR_VERDICT")), 1);
assert_eq!(verdict_exit_code(&v_with_label("DM_THRESHOLD_BLOCK")), 1);
}
#[test]
fn verdict_exit_zero_on_allowed_no_findings_label() {
assert_eq!(verdict_exit_code(&v_with_label("ALLOWED_NO_FINDINGS")), 0);
}
#[test]
fn verdict_exit_two_on_insufficient_data_label() {
assert_eq!(verdict_exit_code(&v_with_label("INSUFFICIENT_DATA")), 2);
}
#[test]
fn verdict_exit_two_on_unknown_label_fail_loud() {
assert_eq!(verdict_exit_code(&v_with_label("UNKNOWN_LABEL_XYZ")), 2);
}
#[test]
fn verdict_exit_two_on_unknown_explicit_decision_fail_loud() {
assert_eq!(verdict_exit_code(&v_with_decision("SOME_NEW_LABEL")), 2);
}
}