shape_runtime/project/
permissions.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
8#[serde(untagged)]
9pub enum PermissionPreset {
10 Shorthand(String),
12 Table(PermissionsSection),
14}
15
16#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)]
21pub struct PermissionsSection {
22 #[serde(default, rename = "fs.read")]
23 pub fs_read: Option<bool>,
24 #[serde(default, rename = "fs.write")]
25 pub fs_write: Option<bool>,
26 #[serde(default, rename = "net.connect")]
27 pub net_connect: Option<bool>,
28 #[serde(default, rename = "net.listen")]
29 pub net_listen: Option<bool>,
30 #[serde(default)]
31 pub process: Option<bool>,
32 #[serde(default)]
33 pub env: Option<bool>,
34 #[serde(default)]
35 pub time: Option<bool>,
36 #[serde(default)]
37 pub random: Option<bool>,
38
39 #[serde(default)]
41 pub fs: Option<FsPermissions>,
42 #[serde(default)]
44 pub net: Option<NetPermissions>,
45}
46
47#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)]
49pub struct FsPermissions {
50 #[serde(default)]
52 pub allowed: Vec<String>,
53 #[serde(default)]
55 pub read_only: Vec<String>,
56}
57
58#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)]
60pub struct NetPermissions {
61 #[serde(default)]
63 pub allowed_hosts: Vec<String>,
64}
65
66impl PermissionsSection {
67 pub fn from_shorthand(name: &str) -> Option<Self> {
73 match name {
74 "pure" => Some(Self {
75 fs_read: Some(false),
76 fs_write: Some(false),
77 net_connect: Some(false),
78 net_listen: Some(false),
79 process: Some(false),
80 env: Some(false),
81 time: Some(false),
82 random: Some(false),
83 fs: None,
84 net: None,
85 }),
86 "readonly" => Some(Self {
87 fs_read: Some(true),
88 fs_write: Some(false),
89 net_connect: Some(false),
90 net_listen: Some(false),
91 process: Some(false),
92 env: Some(true),
93 time: Some(true),
94 random: Some(false),
95 fs: None,
96 net: None,
97 }),
98 "full" => Some(Self {
99 fs_read: Some(true),
100 fs_write: Some(true),
101 net_connect: Some(true),
102 net_listen: Some(true),
103 process: Some(true),
104 env: Some(true),
105 time: Some(true),
106 random: Some(true),
107 fs: None,
108 net: None,
109 }),
110 _ => None,
111 }
112 }
113
114 pub fn to_permission_set(&self) -> shape_abi_v1::PermissionSet {
118 use shape_abi_v1::Permission;
119 let mut set = shape_abi_v1::PermissionSet::pure();
120 if self.fs_read.unwrap_or(true) {
121 set.insert(Permission::FsRead);
122 }
123 if self.fs_write.unwrap_or(true) {
124 set.insert(Permission::FsWrite);
125 }
126 if self.net_connect.unwrap_or(true) {
127 set.insert(Permission::NetConnect);
128 }
129 if self.net_listen.unwrap_or(true) {
130 set.insert(Permission::NetListen);
131 }
132 if self.process.unwrap_or(true) {
133 set.insert(Permission::Process);
134 }
135 if self.env.unwrap_or(true) {
136 set.insert(Permission::Env);
137 }
138 if self.time.unwrap_or(true) {
139 set.insert(Permission::Time);
140 }
141 if self.random.unwrap_or(true) {
142 set.insert(Permission::Random);
143 }
144 if self.fs.as_ref().map_or(false, |fs| {
146 !fs.allowed.is_empty() || !fs.read_only.is_empty()
147 }) {
148 set.insert(Permission::FsScoped);
149 }
150 if self
151 .net
152 .as_ref()
153 .map_or(false, |net| !net.allowed_hosts.is_empty())
154 {
155 set.insert(Permission::NetScoped);
156 }
157 set
158 }
159
160 pub fn to_scope_constraints(&self) -> shape_abi_v1::ScopeConstraints {
162 let mut constraints = shape_abi_v1::ScopeConstraints::none();
163 if let Some(ref fs) = self.fs {
164 let mut paths = fs.allowed.clone();
165 paths.extend(fs.read_only.iter().cloned());
166 constraints.allowed_paths = paths;
167 }
168 if let Some(ref net) = self.net {
169 constraints.allowed_hosts = net.allowed_hosts.clone();
170 }
171 constraints
172 }
173}