unifly-api 0.9.2

Async Rust client, reactive data layer, and domain model for UniFi controller APIs
Documentation
pub(super) fn redact_sensitive_value(value: &serde_json::Value) -> serde_json::Value {
    match value {
        serde_json::Value::Object(map) => serde_json::Value::Object(
            map.iter()
                .map(|(key, value)| {
                    (
                        key.clone(),
                        if should_redact_field(key) {
                            serde_json::Value::String("***REDACTED***".into())
                        } else {
                            redact_sensitive_value(value)
                        },
                    )
                })
                .collect(),
        ),
        serde_json::Value::Array(values) => {
            serde_json::Value::Array(values.iter().map(redact_sensitive_value).collect())
        }
        _ => value.clone(),
    }
}

fn should_redact_field(key: &str) -> bool {
    let key = key.to_ascii_lowercase();
    [
        "private",
        "password",
        "secret",
        "token",
        "psk",
        "shared_key",
        "certificate",
        "dh_key",
    ]
    .into_iter()
    .any(|needle| key.contains(needle))
}

#[cfg(test)]
mod tests {
    use super::redact_sensitive_value;

    #[test]
    fn redact_sensitive_value_masks_nested_vpn_secrets() {
        let redacted = redact_sensitive_value(&serde_json::json!({
            "enabled": true,
            "public_key": "keep-me",
            "x_private_key": "secret",
            "nested": {
                "psk": "hide-me",
                "certificatePem": "hide-me-too"
            }
        }));

        assert_eq!(
            redacted.get("enabled").and_then(serde_json::Value::as_bool),
            Some(true)
        );
        assert_eq!(
            redacted
                .get("public_key")
                .and_then(serde_json::Value::as_str),
            Some("keep-me")
        );
        assert_eq!(
            redacted
                .get("x_private_key")
                .and_then(serde_json::Value::as_str),
            Some("***REDACTED***")
        );
        assert_eq!(redacted["nested"]["psk"].as_str(), Some("***REDACTED***"));
        assert_eq!(
            redacted["nested"]["certificatePem"].as_str(),
            Some("***REDACTED***")
        );
    }
}