1use deno_permissions::{PermissionCheckError, PermissionDeniedError};
2use std::{
3 borrow::Cow,
4 collections::HashSet,
5 path::{Path, PathBuf},
6 sync::{Arc, RwLock},
7};
8
9pub struct PermissionDenied {
13 pub access: String,
15
16 pub name: &'static str,
18}
19impl PermissionDenied {
20 pub fn new(access: impl ToString, reason: &'static str) -> Self {
22 Self {
23 access: access.to_string(),
24 name: reason,
25 }
26 }
27
28 pub fn oops<T>(access: impl ToString) -> Result<T, Self> {
33 Err(Self::new(access, "Not Allowed"))
34 }
35}
36
37impl From<PermissionDenied> for PermissionCheckError {
39 fn from(e: PermissionDenied) -> Self {
40 PermissionCheckError::PermissionDenied(PermissionDeniedError {
41 access: e.access,
42 name: e.name,
43 })
44 }
45}
46
47#[derive(Debug, Clone, Copy, Default)]
51pub struct DefaultWebPermissions;
52impl WebPermissions for DefaultWebPermissions {
53 fn allow_hrtime(&self) -> bool {
54 true
55 }
56
57 fn check_url(&self, url: &deno_core::url::Url, api_name: &str) -> Result<(), PermissionDenied> {
58 Ok(())
59 }
60
61 fn check_open<'a>(
62 &self,
63 resolved: bool,
64 read: bool,
65 write: bool,
66 path: &'a Path,
67 api_name: &str,
68 ) -> Option<std::borrow::Cow<'a, Path>> {
69 Some(Cow::Borrowed(path))
70 }
71
72 fn check_read<'a>(
73 &self,
74 p: &'a Path,
75 api_name: Option<&str>,
76 ) -> Result<Cow<'a, Path>, PermissionDenied> {
77 Ok(Cow::Borrowed(p))
78 }
79
80 fn check_read_all(&self, api_name: Option<&str>) -> Result<(), PermissionDenied> {
81 Ok(())
82 }
83
84 fn check_read_blind(
85 &self,
86 p: &Path,
87 display: &str,
88 api_name: &str,
89 ) -> Result<(), PermissionDenied> {
90 Ok(())
91 }
92
93 fn check_write<'a>(
94 &self,
95 p: &'a Path,
96 api_name: Option<&str>,
97 ) -> Result<Cow<'a, Path>, PermissionDenied> {
98 Ok(Cow::Borrowed(p))
99 }
100
101 fn check_write_all(&self, api_name: &str) -> Result<(), PermissionDenied> {
102 Ok(())
103 }
104
105 fn check_write_blind(
106 &self,
107 p: &Path,
108 display: &str,
109 api_name: &str,
110 ) -> Result<(), PermissionDenied> {
111 Ok(())
112 }
113
114 fn check_write_partial(
115 &self,
116 path: &str,
117 api_name: &str,
118 ) -> Result<std::path::PathBuf, PermissionDenied> {
119 Ok(PathBuf::from(path))
120 }
121
122 fn check_host(
123 &self,
124 host: &str,
125 port: Option<u16>,
126 api_name: &str,
127 ) -> Result<(), PermissionDenied> {
128 Ok(())
129 }
130
131 fn check_sys(
132 &self,
133 kind: SystemsPermissionKind,
134 api_name: &str,
135 ) -> Result<(), PermissionDenied> {
136 Ok(())
137 }
138
139 fn check_env(&self, var: &str) -> Result<(), PermissionDenied> {
140 Ok(())
141 }
142
143 fn check_exec(&self) -> Result<(), PermissionDenied> {
144 Ok(())
145 }
146}
147
148#[derive(Clone, Default, Debug)]
150#[allow(clippy::struct_excessive_bools)]
151struct AllowlistWebPermissionsSet {
152 pub hrtime: bool,
153 pub exec: bool,
154 pub read_all: bool,
155 pub write_all: bool,
156 pub url: HashSet<String>,
157 pub openr_paths: HashSet<String>,
158 pub openw_paths: HashSet<String>,
159 pub envs: HashSet<String>,
160 pub sys: HashSet<SystemsPermissionKind>,
161 pub read_paths: HashSet<String>,
162 pub write_paths: HashSet<String>,
163 pub hosts: HashSet<String>,
164}
165
166#[derive(Clone, Default, Debug)]
172pub struct AllowlistWebPermissions(Arc<RwLock<AllowlistWebPermissionsSet>>);
173impl AllowlistWebPermissions {
174 #[must_use]
176 pub fn new() -> Self {
177 Self(Arc::new(RwLock::new(AllowlistWebPermissionsSet::default())))
178 }
179
180 fn borrow(&self) -> std::sync::RwLockReadGuard<AllowlistWebPermissionsSet> {
181 self.0.read().expect("Could not lock permissions")
182 }
183
184 fn borrow_mut(&self) -> std::sync::RwLockWriteGuard<AllowlistWebPermissionsSet> {
185 self.0.write().expect("Could not lock permissions")
186 }
187
188 pub fn set_hrtime(&self, value: bool) {
192 self.borrow_mut().hrtime = value;
193 }
194
195 pub fn set_exec(&self, value: bool) {
199 self.borrow_mut().exec = value;
200 }
201
202 pub fn set_read_all(&self, value: bool) {
206 self.borrow_mut().read_all = value;
207 }
208
209 pub fn set_write_all(&self, value: bool) {
213 self.borrow_mut().write_all = value;
214 }
215
216 pub fn allow_open(&self, path: &str, read: bool, write: bool) {
221 if read {
222 self.borrow_mut().openr_paths.insert(path.to_string());
223 }
224 if write {
225 self.borrow_mut().openw_paths.insert(path.to_string());
226 }
227 }
228
229 pub fn allow_url(&self, url: &str) {
231 self.borrow_mut().url.insert(url.to_string());
232 }
233
234 pub fn deny_url(&self, url: &str) {
236 self.borrow_mut().url.remove(url);
237 }
238
239 pub fn allow_read(&self, path: &str) {
241 self.borrow_mut().read_paths.insert(path.to_string());
242 }
243
244 pub fn deny_read(&self, path: &str) {
246 self.borrow_mut().read_paths.remove(path);
247 }
248
249 pub fn allow_write(&self, path: &str) {
251 self.borrow_mut().write_paths.insert(path.to_string());
252 }
253
254 pub fn deny_write(&self, path: &str) {
256 self.borrow_mut().write_paths.remove(path);
257 }
258
259 pub fn allow_host(&self, host: &str) {
261 self.borrow_mut().hosts.insert(host.to_string());
262 }
263
264 pub fn deny_host(&self, host: &str) {
266 self.borrow_mut().hosts.remove(host);
267 }
268
269 pub fn allow_env(&self, var: &str) {
271 self.borrow_mut().envs.insert(var.to_string());
272 }
273
274 pub fn deny_env(&self, var: &str) {
276 self.borrow_mut().envs.remove(var);
277 }
278
279 pub fn allow_sys(&self, kind: SystemsPermissionKind) {
281 self.borrow_mut().sys.insert(kind);
282 }
283
284 pub fn deny_sys(&self, kind: SystemsPermissionKind) {
286 self.borrow_mut().sys.remove(&kind);
287 }
288}
289impl WebPermissions for AllowlistWebPermissions {
290 fn allow_hrtime(&self) -> bool {
291 self.borrow().hrtime
292 }
293
294 fn check_host(
295 &self,
296 host: &str,
297 port: Option<u16>,
298 api_name: &str,
299 ) -> Result<(), PermissionDenied> {
300 if self.borrow().hosts.contains(host) {
301 Ok(())
302 } else {
303 PermissionDenied::oops(host)?
304 }
305 }
306
307 fn check_url(&self, url: &deno_core::url::Url, api_name: &str) -> Result<(), PermissionDenied> {
308 if self.borrow().url.contains(url.as_str()) {
309 Ok(())
310 } else {
311 PermissionDenied::oops(url)?
312 }
313 }
314
315 fn check_read<'a>(
316 &self,
317 p: &'a Path,
318 api_name: Option<&str>,
319 ) -> Result<Cow<'a, Path>, PermissionDenied> {
320 let inst = self.borrow();
321 if inst.read_all && inst.read_paths.contains(p.to_str().unwrap()) {
322 Ok(Cow::Borrowed(p))
323 } else {
324 PermissionDenied::oops(p.display())?
325 }
326 }
327
328 fn check_write<'a>(
329 &self,
330 p: &'a Path,
331 api_name: Option<&str>,
332 ) -> Result<Cow<'a, Path>, PermissionDenied> {
333 let inst = self.borrow();
334 if inst.write_all && inst.write_paths.contains(p.to_str().unwrap()) {
335 Ok(Cow::Borrowed(p))
336 } else {
337 PermissionDenied::oops(p.display())?
338 }
339 }
340
341 fn check_open<'a>(
342 &self,
343 resolved: bool,
344 read: bool,
345 write: bool,
346 path: &'a Path,
347 api_name: &str,
348 ) -> Option<std::borrow::Cow<'a, Path>> {
349 let path = path.to_str().unwrap();
350 if read && !self.borrow().openr_paths.contains(path) {
351 return None;
352 }
353 if write && !self.borrow().openw_paths.contains(path) {
354 return None;
355 }
356 Some(Cow::Borrowed(path.as_ref()))
357 }
358
359 fn check_read_all(&self, api_name: Option<&str>) -> Result<(), PermissionDenied> {
360 if self.borrow().read_all {
361 Ok(())
362 } else {
363 PermissionDenied::oops("read_all")?
364 }
365 }
366
367 fn check_read_blind(
368 &self,
369 p: &Path,
370 display: &str,
371 api_name: &str,
372 ) -> Result<(), PermissionDenied> {
373 if !self.borrow().read_all {
374 return PermissionDenied::oops("read_all")?;
375 }
376 self.check_read(p, Some(api_name))?;
377 Ok(())
378 }
379
380 fn check_write_all(&self, api_name: &str) -> Result<(), PermissionDenied> {
381 if self.borrow().write_all {
382 Ok(())
383 } else {
384 PermissionDenied::oops("write_all")?
385 }
386 }
387
388 fn check_write_blind(
389 &self,
390 path: &Path,
391 display: &str,
392 api_name: &str,
393 ) -> Result<(), PermissionDenied> {
394 self.check_write(Path::new(path), Some(api_name))?;
395 Ok(())
396 }
397
398 fn check_write_partial(
399 &self,
400 path: &str,
401 api_name: &str,
402 ) -> Result<std::path::PathBuf, PermissionDenied> {
403 let p = self.check_write(Path::new(path), Some(api_name))?;
404 Ok(p.into_owned())
405 }
406
407 fn check_sys(
408 &self,
409 kind: SystemsPermissionKind,
410 api_name: &str,
411 ) -> Result<(), PermissionDenied> {
412 if self.borrow().sys.contains(&kind) {
413 Ok(())
414 } else {
415 PermissionDenied::oops(kind.as_str())?
416 }
417 }
418
419 fn check_env(&self, var: &str) -> Result<(), PermissionDenied> {
420 if self.borrow().envs.contains(var) {
421 Ok(())
422 } else {
423 PermissionDenied::oops(var)?
424 }
425 }
426
427 fn check_exec(&self) -> Result<(), PermissionDenied> {
428 if self.borrow().exec {
429 Ok(())
430 } else {
431 PermissionDenied::oops("ffi")?
432 }
433 }
434}
435
436pub trait WebPermissions: std::fmt::Debug + Send + Sync {
440 fn allow_hrtime(&self) -> bool;
444
445 fn check_url(&self, url: &deno_core::url::Url, api_name: &str) -> Result<(), PermissionDenied>;
450
451 fn check_open<'a>(
455 &self,
456 resolved: bool,
457 read: bool,
458 write: bool,
459 path: &'a Path,
460 api_name: &str,
461 ) -> Option<std::borrow::Cow<'a, Path>>;
462
463 fn check_read<'a>(
468 &self,
469 p: &'a Path,
470 api_name: Option<&str>,
471 ) -> Result<Cow<'a, Path>, PermissionDenied>;
472
473 fn check_read_all(&self, api_name: Option<&str>) -> Result<(), PermissionDenied>;
480
481 fn check_read_blind(
486 &self,
487 p: &Path,
488 display: &str,
489 api_name: &str,
490 ) -> Result<(), PermissionDenied>;
491
492 fn check_write<'a>(
497 &self,
498 p: &'a Path,
499 api_name: Option<&str>,
500 ) -> Result<Cow<'a, Path>, PermissionDenied>;
501
502 fn check_write_all(&self, api_name: &str) -> Result<(), PermissionDenied>;
509
510 fn check_write_blind(
515 &self,
516 p: &Path,
517 display: &str,
518 api_name: &str,
519 ) -> Result<(), PermissionDenied>;
520
521 fn check_write_partial(
526 &self,
527 path: &str,
528 api_name: &str,
529 ) -> Result<std::path::PathBuf, PermissionDenied>;
530
531 fn check_host(
536 &self,
537 host: &str,
538 port: Option<u16>,
539 api_name: &str,
540 ) -> Result<(), PermissionDenied>;
541
542 fn check_sys(
547 &self,
548 kind: SystemsPermissionKind,
549 api_name: &str,
550 ) -> Result<(), PermissionDenied>;
551
552 fn check_env(&self, var: &str) -> Result<(), PermissionDenied>;
559
560 fn check_exec(&self) -> Result<(), PermissionDenied>;
565}
566
567macro_rules! impl_sys_permission_kinds {
568 ($($kind:ident($name:literal)),+ $(,)?) => {
569 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
574 pub enum SystemsPermissionKind {
575 $(
576 #[doc = stringify!($kind)]
577 $kind,
578 )+
579
580 Other(String),
582 }
583 impl SystemsPermissionKind {
584 #[must_use]
586 pub fn new(s: &str) -> Self {
587 match s {
588 $( $name => Self::$kind, )+
589 _ => Self::Other(s.to_string()),
590 }
591 }
592
593 #[must_use]
595 pub fn as_str(&self) -> &str {
596 match self {
597 $( Self::$kind => $name, )+
598 Self::Other(s) => &s,
599 }
600 }
601 }
602 };
603}
604
605impl_sys_permission_kinds!(
606 LoadAvg("loadavg"),
607 Hostname("hostname"),
608 OsRelease("osRelease"),
609 Networkinterfaces("networkInterfaces"),
610 StatFs("statfs"),
611 GetPriority("getPriority"),
612 SystemMemoryInfo("systemMemoryInfo"),
613 Gid("gid"),
614 Uid("uid"),
615 OsUptime("osUptime"),
616 SetPriority("setPriority"),
617 UserInfo("userInfo"),
618 GetEGid("getegid"),
619 Cpus("cpus"),
620 HomeDir("homeDir"),
621 Inspector("inspector"),
622);
623
624#[derive(Clone, Debug)]
625pub struct PermissionsContainer(pub Arc<dyn WebPermissions>);
626impl deno_web::TimersPermission for PermissionsContainer {
627 fn allow_hrtime(&mut self) -> bool {
628 self.0.allow_hrtime()
629 }
630}
631impl deno_fetch::FetchPermissions for PermissionsContainer {
632 fn check_net_url(
633 &mut self,
634 url: &reqwest::Url,
635 api_name: &str,
636 ) -> Result<(), PermissionCheckError> {
637 self.0.check_url(url, api_name)?;
638 Ok(())
639 }
640
641 fn check_read<'a>(
642 &mut self,
643 p: &'a Path,
644 api_name: &str,
645 ) -> Result<Cow<'a, Path>, PermissionCheckError> {
646 let p = self.0.check_read(p, Some(api_name))?;
647 Ok(p)
648 }
649}
650impl deno_net::NetPermissions for PermissionsContainer {
651 fn check_net<T: AsRef<str>>(
652 &mut self,
653 host: &(T, Option<u16>),
654 api_name: &str,
655 ) -> Result<(), PermissionCheckError> {
656 self.0.check_host(host.0.as_ref(), host.1, api_name)?;
657 Ok(())
658 }
659
660 fn check_read(&mut self, p: &str, api_name: &str) -> Result<PathBuf, PermissionCheckError> {
661 let p = self
662 .0
663 .check_read(Path::new(p), Some(api_name))
664 .map(std::borrow::Cow::into_owned)?;
665 Ok(p)
666 }
667
668 fn check_write(&mut self, p: &str, api_name: &str) -> Result<PathBuf, PermissionCheckError> {
669 let p = self
670 .0
671 .check_write(Path::new(p), Some(api_name))
672 .map(std::borrow::Cow::into_owned)?;
673 Ok(p)
674 }
675
676 fn check_write_path<'a>(
677 &mut self,
678 p: &'a Path,
679 api_name: &str,
680 ) -> Result<Cow<'a, Path>, PermissionCheckError> {
681 let p = self.0.check_write(p, Some(api_name))?;
682 Ok(p)
683 }
684}