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}