use serde_json::Value;
#[must_use]
pub fn decision_object_allowed(decision_obj: &serde_json::Map<String, Value>) -> Option<bool> {
decision_obj
.get("allowed")
.and_then(|v| v.as_bool())
.or_else(|| decision_obj.get("passed").and_then(|v| v.as_bool()))
.or_else(|| {
decision_obj
.get("denied")
.and_then(|v| v.as_bool())
.map(|v| !v)
})
.or_else(|| {
decision_obj
.get("blocked")
.and_then(|v| v.as_bool())
.map(|v| !v)
})
}
#[must_use]
pub fn decision_object_is_warn(decision_obj: &serde_json::Map<String, Value>) -> bool {
if decision_obj
.get("warn")
.or_else(|| decision_obj.get("warning"))
.and_then(|v| v.as_bool())
.unwrap_or(false)
{
return true;
}
if matches!(
decision_obj
.get("verdict")
.or_else(|| decision_obj.get("decision"))
.and_then(|v| v.as_str())
.map(|s| s.to_ascii_lowercase()),
Some(v) if matches!(v.as_str(), "warn" | "warning" | "warned" | "logged")
) {
return true;
}
if decision_object_allowed(decision_obj) != Some(true) {
return false;
}
matches!(
decision_obj
.get("severity")
.and_then(severity_from_value)
.map(|s| s.to_ascii_lowercase()),
Some(v) if matches!(v.as_str(), "warn" | "warning")
)
}
#[must_use]
pub fn severity_from_value(value: &Value) -> Option<String> {
match value {
Value::String(s) => Some(s.to_string()),
Value::Object(obj) => obj
.get("level")
.or_else(|| obj.get("name"))
.or_else(|| obj.get("value"))
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn allowed_aliases_are_supported() {
let passed = json!({ "passed": false }).as_object().cloned().unwrap();
assert_eq!(decision_object_allowed(&passed), Some(false));
let denied = json!({ "denied": true }).as_object().cloned().unwrap();
assert_eq!(decision_object_allowed(&denied), Some(false));
let blocked = json!({ "blocked": false }).as_object().cloned().unwrap();
assert_eq!(decision_object_allowed(&blocked), Some(true));
}
#[test]
fn decision_field_warn_string_is_treated_as_warn() {
let decision_obj = json!({ "decision": "warn" }).as_object().cloned().unwrap();
assert!(decision_object_is_warn(&decision_obj));
}
#[test]
fn warning_severity_requires_allowed_true() {
let no_allowed = json!({ "severity": "warning" })
.as_object()
.cloned()
.unwrap();
assert!(!decision_object_is_warn(&no_allowed));
let allowed_true = json!({ "allowed": true, "severity": "warning" })
.as_object()
.cloned()
.unwrap();
assert!(decision_object_is_warn(&allowed_true));
let allowed_false = json!({ "allowed": false, "severity": "warning" })
.as_object()
.cloned()
.unwrap();
assert!(!decision_object_is_warn(&allowed_false));
}
}