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