Skip to main content

weft_core/app/
policy.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
5#[serde(rename_all = "lowercase")]
6pub enum AppProfile {
7    #[default]
8    Safe,
9    Developer,
10    Trusted,
11}
12
13impl AppProfile {
14    pub fn from_str_loose(s: &str) -> Self {
15        match s.to_lowercase().as_str() {
16            "developer" | "dev" => Self::Developer,
17            "trusted" | "trust" | "full" => Self::Trusted,
18            _ => Self::Safe,
19        }
20    }
21
22    pub fn as_str(&self) -> &'static str {
23        match self {
24            Self::Safe => "safe",
25            Self::Developer => "developer",
26            Self::Trusted => "trusted",
27        }
28    }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct PolicyDecision {
33    pub allowed: bool,
34    pub reason: String,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct CapabilityPolicyRule {
39    pub min_profile: AppProfile,
40    pub description: String,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, Default)]
44pub struct CorePolicy {
45    pub rules: HashMap<String, CapabilityPolicyRule>,
46}
47
48impl CorePolicy {
49    pub fn default_policy() -> Self {
50        let mut rules = HashMap::new();
51
52        rules.insert(
53            "core.files".into(),
54            CapabilityPolicyRule {
55                min_profile: AppProfile::Developer,
56                description: "File system read/write operations".into(),
57            },
58        );
59
60        rules.insert(
61            "core.execution".into(),
62            CapabilityPolicyRule {
63                min_profile: AppProfile::Developer,
64                description: "Command execution and process management".into(),
65            },
66        );
67
68        rules.insert(
69            "core.native_execution".into(),
70            CapabilityPolicyRule {
71                min_profile: AppProfile::Trusted,
72                description: "Native/dll/so package execution".into(),
73            },
74        );
75
76        Self { rules }
77    }
78
79    pub fn check(&self, capability: &str, profile: AppProfile) -> PolicyDecision {
80        if let Some(rule) = self.rules.get(capability) {
81            let allowed = profile_rank(profile) >= profile_rank(rule.min_profile);
82            PolicyDecision {
83                allowed,
84                reason: if allowed {
85                    format!(
86                        "Profile '{}' meets minimum '{}' for '{}'",
87                        profile.as_str(),
88                        rule.min_profile.as_str(),
89                        capability
90                    )
91                } else {
92                    format!(
93                        "Profile '{}' does not meet minimum '{}' required for '{}'",
94                        profile.as_str(),
95                        rule.min_profile.as_str(),
96                        capability
97                    )
98                },
99            }
100        } else {
101            PolicyDecision {
102                allowed: true,
103                reason: format!("No policy restriction on '{}'", capability),
104            }
105        }
106    }
107}
108
109fn profile_rank(profile: AppProfile) -> u8 {
110    match profile {
111        AppProfile::Safe => 0,
112        AppProfile::Developer => 1,
113        AppProfile::Trusted => 2,
114    }
115}