1use std::{
2 borrow::Cow,
3 collections::HashSet,
4 path::{Path, PathBuf},
5 sync::{Arc, RwLock},
6};
7
8use deno_permissions::PermissionDeniedError as DenoPermissionDeniedError;
9
10pub fn oops<T>(msg: impl std::fmt::Display) -> Result<T, DenoPermissionDeniedError> {
11 Err(DenoPermissionDeniedError { access: msg.to_string(), name: "" })
12}
13
14#[derive(Debug, Clone, Copy, Default)]
18pub struct DefaultWebPermissions;
19impl WebPermissions for DefaultWebPermissions {
20 fn allow_hrtime(&self) -> bool {
21 true
22 }
23
24 fn check_url(
25 &self,
26 url: &deno_core::url::Url,
27 api_name: &str,
28 ) -> Result<(), DenoPermissionDeniedError> {
29 Ok(())
30 }
31
32 fn check_open<'a>(
33 &self,
34 resolved: bool,
35 read: bool,
36 write: bool,
37 path: &'a Path,
38 api_name: &str,
39 ) -> Option<std::borrow::Cow<'a, Path>> {
40 Some(Cow::Borrowed(path))
41 }
42
43 fn check_read<'a>(
44 &self,
45 p: &'a Path,
46 api_name: Option<&str>,
47 ) -> Result<Cow<'a, Path>, DenoPermissionDeniedError> {
48 Ok(Cow::Borrowed(p))
49 }
50
51 fn check_read_all(&self, api_name: Option<&str>) -> Result<(), DenoPermissionDeniedError> {
52 Ok(())
53 }
54
55 fn check_read_blind(
56 &self,
57 p: &Path,
58 display: &str,
59 api_name: &str,
60 ) -> Result<(), DenoPermissionDeniedError> {
61 Ok(())
62 }
63
64 fn check_write<'a>(
65 &self,
66 p: &'a Path,
67 api_name: Option<&str>,
68 ) -> Result<Cow<'a, Path>, DenoPermissionDeniedError> {
69 Ok(Cow::Borrowed(p))
70 }
71
72 fn check_write_all(&self, api_name: &str) -> Result<(), DenoPermissionDeniedError> {
73 Ok(())
74 }
75
76 fn check_write_blind(
77 &self,
78 p: &Path,
79 display: &str,
80 api_name: &str,
81 ) -> Result<(), DenoPermissionDeniedError> {
82 Ok(())
83 }
84
85 fn check_write_partial(
86 &self,
87 path: &str,
88 api_name: &str,
89 ) -> Result<std::path::PathBuf, DenoPermissionDeniedError> {
90 Ok(PathBuf::from(path))
91 }
92
93 fn check_host(
94 &self,
95 host: &str,
96 port: Option<u16>,
97 api_name: &str,
98 ) -> Result<(), DenoPermissionDeniedError> {
99 Ok(())
100 }
101
102 fn check_sys(
103 &self,
104 kind: SystemsPermissionKind,
105 api_name: &str,
106 ) -> Result<(), DenoPermissionDeniedError> {
107 Ok(())
108 }
109
110 fn check_env(&self, var: &str) -> Result<(), DenoPermissionDeniedError> {
111 Ok(())
112 }
113
114 fn check_exec(&self) -> Result<(), DenoPermissionDeniedError> {
115 Ok(())
116 }
117}
118
119#[derive(Clone, Default, Debug)]
121#[allow(clippy::struct_excessive_bools)]
122struct AllowlistWebPermissionsSet {
123 pub hrtime: bool,
124 pub exec: bool,
125 pub read_all: bool,
126 pub write_all: bool,
127 pub url: HashSet<String>,
128 pub openr_paths: HashSet<String>,
129 pub openw_paths: HashSet<String>,
130 pub envs: HashSet<String>,
131 pub sys: HashSet<SystemsPermissionKind>,
132 pub read_paths: HashSet<String>,
133 pub write_paths: HashSet<String>,
134 pub hosts: HashSet<String>,
135}
136
137#[derive(Clone, Default, Debug)]
143pub struct AllowlistWebPermissions(Arc<RwLock<AllowlistWebPermissionsSet>>);
144impl AllowlistWebPermissions {
145 #[must_use]
147 pub fn new() -> Self {
148 Self(Arc::new(RwLock::new(AllowlistWebPermissionsSet::default())))
149 }
150
151 fn borrow(&self) -> std::sync::RwLockReadGuard<AllowlistWebPermissionsSet> {
152 self.0.read().expect("Could not lock permissions")
153 }
154
155 fn borrow_mut(&self) -> std::sync::RwLockWriteGuard<AllowlistWebPermissionsSet> {
156 self.0.write().expect("Could not lock permissions")
157 }
158
159 pub fn set_hrtime(&self, value: bool) {
163 self.borrow_mut().hrtime = value;
164 }
165
166 pub fn set_exec(&self, value: bool) {
170 self.borrow_mut().exec = value;
171 }
172
173 pub fn set_read_all(&self, value: bool) {
177 self.borrow_mut().read_all = value;
178 }
179
180 pub fn set_write_all(&self, value: bool) {
184 self.borrow_mut().write_all = value;
185 }
186
187 pub fn allow_open(&self, path: &str, read: bool, write: bool) {
192 if read {
193 self.borrow_mut().openr_paths.insert(path.to_string());
194 }
195 if write {
196 self.borrow_mut().openw_paths.insert(path.to_string());
197 }
198 }
199
200 pub fn allow_url(&self, url: &str) {
202 self.borrow_mut().url.insert(url.to_string());
203 }
204
205 pub fn deny_url(&self, url: &str) {
207 self.borrow_mut().url.remove(url);
208 }
209
210 pub fn allow_read(&self, path: &str) {
212 self.borrow_mut().read_paths.insert(path.to_string());
213 }
214
215 pub fn deny_read(&self, path: &str) {
217 self.borrow_mut().read_paths.remove(path);
218 }
219
220 pub fn allow_write(&self, path: &str) {
222 self.borrow_mut().write_paths.insert(path.to_string());
223 }
224
225 pub fn deny_write(&self, path: &str) {
227 self.borrow_mut().write_paths.remove(path);
228 }
229
230 pub fn allow_host(&self, host: &str) {
232 self.borrow_mut().hosts.insert(host.to_string());
233 }
234
235 pub fn deny_host(&self, host: &str) {
237 self.borrow_mut().hosts.remove(host);
238 }
239
240 pub fn allow_env(&self, var: &str) {
242 self.borrow_mut().envs.insert(var.to_string());
243 }
244
245 pub fn deny_env(&self, var: &str) {
247 self.borrow_mut().envs.remove(var);
248 }
249
250 pub fn allow_sys(&self, kind: SystemsPermissionKind) {
252 self.borrow_mut().sys.insert(kind);
253 }
254
255 pub fn deny_sys(&self, kind: SystemsPermissionKind) {
257 self.borrow_mut().sys.remove(&kind);
258 }
259}
260impl WebPermissions for AllowlistWebPermissions {
261 fn allow_hrtime(&self) -> bool {
262 self.borrow().hrtime
263 }
264
265 fn check_host(
266 &self,
267 host: &str,
268 port: Option<u16>,
269 api_name: &str,
270 ) -> Result<(), DenoPermissionDeniedError> {
271 if self.borrow().hosts.contains(host) {
272 Ok(())
273 } else {
274 oops(host)?
275 }
276 }
277
278 fn check_url(
279 &self,
280 url: &deno_core::url::Url,
281 api_name: &str,
282 ) -> Result<(), DenoPermissionDeniedError> {
283 if self.borrow().url.contains(url.as_str()) {
284 Ok(())
285 } else {
286 oops(url)?
287 }
288 }
289
290 fn check_read<'a>(
291 &self,
292 p: &'a Path,
293 api_name: Option<&str>,
294 ) -> Result<Cow<'a, Path>, DenoPermissionDeniedError> {
295 let inst = self.borrow();
296 if inst.read_all && inst.read_paths.contains(p.to_str().unwrap()) {
297 Ok(Cow::Borrowed(p))
298 } else {
299 oops(p.display())?
300 }
301 }
302
303 fn check_write<'a>(
304 &self,
305 p: &'a Path,
306 api_name: Option<&str>,
307 ) -> Result<Cow<'a, Path>, DenoPermissionDeniedError> {
308 let inst = self.borrow();
309 if inst.write_all && inst.write_paths.contains(p.to_str().unwrap()) {
310 Ok(Cow::Borrowed(p))
311 } else {
312 oops(p.display())?
313 }
314 }
315
316 fn check_open<'a>(
317 &self,
318 resolved: bool,
319 read: bool,
320 write: bool,
321 path: &'a Path,
322 api_name: &str,
323 ) -> Option<std::borrow::Cow<'a, Path>> {
324 let path = path.to_str().unwrap();
325 if read && !self.borrow().openr_paths.contains(path) {
326 return None;
327 }
328 if write && !self.borrow().openw_paths.contains(path) {
329 return None;
330 }
331 Some(Cow::Borrowed(path.as_ref()))
332 }
333
334 fn check_read_all(&self, api_name: Option<&str>) -> Result<(), DenoPermissionDeniedError> {
335 if self.borrow().read_all {
336 Ok(())
337 } else {
338 oops("read_all")?
339 }
340 }
341
342 fn check_read_blind(
343 &self,
344 p: &Path,
345 display: &str,
346 api_name: &str,
347 ) -> Result<(), DenoPermissionDeniedError> {
348 if !self.borrow().read_all {
349 return oops("read_all")?;
350 }
351 self.check_read(p, Some(api_name))?;
352 Ok(())
353 }
354
355 fn check_write_all(&self, api_name: &str) -> Result<(), DenoPermissionDeniedError> {
356 if self.borrow().write_all {
357 Ok(())
358 } else {
359 oops("write_all")?
360 }
361 }
362
363 fn check_write_blind(
364 &self,
365 path: &Path,
366 display: &str,
367 api_name: &str,
368 ) -> Result<(), DenoPermissionDeniedError> {
369 self.check_write(Path::new(path), Some(api_name))?;
370 Ok(())
371 }
372
373 fn check_write_partial(
374 &self,
375 path: &str,
376 api_name: &str,
377 ) -> Result<std::path::PathBuf, DenoPermissionDeniedError> {
378 let p = self.check_write(Path::new(path), Some(api_name))?;
379 Ok(p.into_owned())
380 }
381
382 fn check_sys(
383 &self,
384 kind: SystemsPermissionKind,
385 api_name: &str,
386 ) -> Result<(), DenoPermissionDeniedError> {
387 if self.borrow().sys.contains(&kind) {
388 Ok(())
389 } else {
390 oops(kind.as_str())?
391 }
392 }
393
394 fn check_env(&self, var: &str) -> Result<(), DenoPermissionDeniedError> {
395 if self.borrow().envs.contains(var) {
396 Ok(())
397 } else {
398 oops(var)?
399 }
400 }
401
402 fn check_exec(&self) -> Result<(), DenoPermissionDeniedError> {
403 if self.borrow().exec {
404 Ok(())
405 } else {
406 oops("ffi")?
407 }
408 }
409}
410
411pub trait WebPermissions: std::fmt::Debug + Send + Sync {
415 fn allow_hrtime(&self) -> bool;
419
420 fn check_url(
425 &self,
426 url: &deno_core::url::Url,
427 api_name: &str,
428 ) -> Result<(), DenoPermissionDeniedError>;
429
430 fn check_open<'a>(
434 &self,
435 resolved: bool,
436 read: bool,
437 write: bool,
438 path: &'a Path,
439 api_name: &str,
440 ) -> Option<std::borrow::Cow<'a, Path>>;
441
442 fn check_read<'a>(
447 &self,
448 p: &'a Path,
449 api_name: Option<&str>,
450 ) -> Result<Cow<'a, Path>, DenoPermissionDeniedError>;
451
452 fn check_read_all(&self, api_name: Option<&str>) -> Result<(), DenoPermissionDeniedError>;
459
460 fn check_read_blind(
465 &self,
466 p: &Path,
467 display: &str,
468 api_name: &str,
469 ) -> Result<(), DenoPermissionDeniedError>;
470
471 fn check_write<'a>(
476 &self,
477 p: &'a Path,
478 api_name: Option<&str>,
479 ) -> Result<Cow<'a, Path>, DenoPermissionDeniedError>;
480
481 fn check_write_all(&self, api_name: &str) -> Result<(), DenoPermissionDeniedError>;
488
489 fn check_write_blind(
494 &self,
495 p: &Path,
496 display: &str,
497 api_name: &str,
498 ) -> Result<(), DenoPermissionDeniedError>;
499
500 fn check_write_partial(
505 &self,
506 path: &str,
507 api_name: &str,
508 ) -> Result<std::path::PathBuf, DenoPermissionDeniedError>;
509
510 fn check_host(
515 &self,
516 host: &str,
517 port: Option<u16>,
518 api_name: &str,
519 ) -> Result<(), DenoPermissionDeniedError>;
520
521 fn check_sys(
526 &self,
527 kind: SystemsPermissionKind,
528 api_name: &str,
529 ) -> Result<(), DenoPermissionDeniedError>;
530
531 fn check_env(&self, var: &str) -> Result<(), DenoPermissionDeniedError>;
538
539 fn check_exec(&self) -> Result<(), DenoPermissionDeniedError>;
544}
545
546macro_rules! impl_sys_permission_kinds {
547 ($($kind:ident($name:literal)),+ $(,)?) => {
548 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
553 pub enum SystemsPermissionKind {
554 $(
555 #[doc = stringify!($kind)]
556 $kind,
557 )+
558
559 Other(String),
561 }
562 impl SystemsPermissionKind {
563 #[must_use]
565 pub fn new(s: &str) -> Self {
566 match s {
567 $( $name => Self::$kind, )+
568 _ => Self::Other(s.to_string()),
569 }
570 }
571
572 #[must_use]
574 pub fn as_str(&self) -> &str {
575 match self {
576 $( Self::$kind => $name, )+
577 Self::Other(s) => &s,
578 }
579 }
580 }
581 };
582}
583
584impl_sys_permission_kinds!(
585 LoadAvg("loadavg"),
586 Hostname("hostname"),
587 OsRelease("osRelease"),
588 Networkinterfaces("networkInterfaces"),
589 StatFs("statfs"),
590 GetPriority("getPriority"),
591 SystemMemoryInfo("systemMemoryInfo"),
592 Gid("gid"),
593 Uid("uid"),
594 OsUptime("osUptime"),
595 SetPriority("setPriority"),
596 UserInfo("userInfo"),
597 GetEGid("getegid"),
598 Cpus("cpus"),
599 HomeDir("homeDir"),
600 Inspector("inspector"),
601);
602
603#[derive(Clone, Debug)]
604pub struct PermissionsContainer(pub Arc<dyn WebPermissions>);
605impl deno_web::TimersPermission for PermissionsContainer {
606 fn allow_hrtime(&mut self) -> bool {
607 self.0.allow_hrtime()
608 }
609}