deno_permissions/
lib.rs

1// Copyright 2018-2025 the Deno authors. MIT license.
2
3use std::borrow::Cow;
4use std::collections::HashSet;
5use std::ffi::OsStr;
6use std::fmt;
7use std::fmt::Debug;
8use std::hash::Hash;
9use std::net::IpAddr;
10use std::net::Ipv6Addr;
11use std::path::Path;
12use std::path::PathBuf;
13use std::string::ToString;
14use std::sync::Arc;
15
16use capacity_builder::StringBuilder;
17use deno_core::parking_lot::Mutex;
18use deno_core::serde::de;
19use deno_core::serde::Deserialize;
20use deno_core::serde::Deserializer;
21use deno_core::serde::Serialize;
22use deno_core::serde_json;
23use deno_core::unsync::sync::AtomicFlag;
24use deno_core::url::Url;
25use deno_core::ModuleSpecifier;
26use deno_path_util::normalize_path;
27use deno_path_util::url_to_file_path;
28use deno_terminal::colors;
29use fqdn::FQDN;
30use once_cell::sync::Lazy;
31
32pub mod prompter;
33use prompter::permission_prompt;
34pub use prompter::set_prompt_callbacks;
35pub use prompter::set_prompter;
36pub use prompter::PermissionPrompter;
37pub use prompter::PromptCallback;
38pub use prompter::PromptResponse;
39use prompter::PERMISSION_EMOJI;
40
41#[derive(Debug, thiserror::Error)]
42pub enum PermissionDeniedError {
43  #[error("Requires {access}, {}", format_permission_error(.name))]
44  Retryable { access: String, name: &'static str },
45  #[error("Requires {access}, which cannot be granted in this environment")]
46  Fatal { access: String },
47}
48
49fn format_permission_error(name: &'static str) -> String {
50  if is_standalone() {
51    format!("specify the required permissions during compilation using `deno compile --allow-{name}`")
52  } else {
53    format!("run again with the --allow-{name} flag")
54  }
55}
56
57/// Fast exit from permission check routines if this permission
58/// is in the "fully-granted" state.
59macro_rules! skip_check_if_is_permission_fully_granted {
60  ($this:ident) => {
61    if $this.is_allow_all() {
62      return Ok(());
63    }
64  };
65}
66
67#[inline]
68fn resolve_from_known_cwd(path: &Path, cwd: &Path) -> PathBuf {
69  if path.is_absolute() {
70    normalize_path(path)
71  } else {
72    normalize_path(cwd.join(path))
73  }
74}
75
76static DEBUG_LOG_ENABLED: Lazy<bool> =
77  Lazy::new(|| log::log_enabled!(log::Level::Debug));
78
79/// Quadri-state value for storing permission state
80#[derive(
81  Eq, PartialEq, Default, Debug, Clone, Copy, Deserialize, PartialOrd,
82)]
83pub enum PermissionState {
84  Granted = 0,
85  GrantedPartial = 1,
86  #[default]
87  Prompt = 2,
88  Denied = 3,
89}
90
91/// `AllowPartial` prescribes how to treat a permission which is partially
92/// denied due to a `--deny-*` flag affecting a subscope of the queried
93/// permission.
94///
95/// `TreatAsGranted` is used in place of `TreatAsPartialGranted` when we don't
96/// want to wastefully check for partial denials when, say, checking read
97/// access for a file.
98#[derive(Debug, Eq, PartialEq)]
99#[allow(clippy::enum_variant_names)]
100enum AllowPartial {
101  TreatAsGranted,
102  TreatAsDenied,
103  TreatAsPartialGranted,
104}
105
106impl From<bool> for AllowPartial {
107  fn from(value: bool) -> Self {
108    if value {
109      Self::TreatAsGranted
110    } else {
111      Self::TreatAsDenied
112    }
113  }
114}
115
116impl PermissionState {
117  #[inline(always)]
118  fn log_perm_access(
119    name: &'static str,
120    info: impl FnOnce() -> Option<String>,
121  ) {
122    // Eliminates log overhead (when logging is disabled),
123    // log_enabled!(Debug) check in a hot path still has overhead
124    // TODO(AaronO): generalize or upstream this optimization
125    if *DEBUG_LOG_ENABLED {
126      log::debug!(
127        "{}",
128        colors::bold(&format!(
129          "{}️  Granted {}",
130          PERMISSION_EMOJI,
131          Self::fmt_access(name, info)
132        ))
133      );
134    }
135  }
136
137  fn fmt_access(
138    name: &'static str,
139    info: impl FnOnce() -> Option<String>,
140  ) -> String {
141    format!(
142      "{} access{}",
143      name,
144      info().map(|info| format!(" to {info}")).unwrap_or_default(),
145    )
146  }
147
148  fn retryable_error(
149    name: &'static str,
150    info: impl FnOnce() -> Option<String>,
151  ) -> PermissionDeniedError {
152    PermissionDeniedError::Retryable {
153      access: Self::fmt_access(name, info),
154      name,
155    }
156  }
157
158  /// Check the permission state. bool is whether a prompt was issued.
159  #[inline]
160  fn check(
161    self,
162    name: &'static str,
163    api_name: Option<&str>,
164    info: Option<&str>,
165    prompt: bool,
166  ) -> (Result<(), PermissionDeniedError>, bool, bool) {
167    self.check2(name, api_name, || info.map(|s| s.to_string()), prompt)
168  }
169
170  #[inline]
171  fn check2(
172    self,
173    name: &'static str,
174    api_name: Option<&str>,
175    info: impl Fn() -> Option<String>,
176    prompt: bool,
177  ) -> (Result<(), PermissionDeniedError>, bool, bool) {
178    match self {
179      PermissionState::Granted => {
180        Self::log_perm_access(name, info);
181        (Ok(()), false, false)
182      }
183      PermissionState::Prompt if prompt => {
184        let msg = {
185          let info = info();
186          StringBuilder::<String>::build(|builder| {
187            builder.append(name);
188            builder.append(" access");
189            if let Some(info) = &info {
190              builder.append(" to ");
191              builder.append(info);
192            }
193          })
194          .unwrap()
195        };
196        match permission_prompt(&msg, name, api_name, true) {
197          PromptResponse::Allow => {
198            Self::log_perm_access(name, info);
199            (Ok(()), true, false)
200          }
201          PromptResponse::AllowAll => {
202            Self::log_perm_access(name, info);
203            (Ok(()), true, true)
204          }
205          PromptResponse::Deny => {
206            (Err(Self::retryable_error(name, info)), true, false)
207          }
208        }
209      }
210      _ => (Err(Self::retryable_error(name, info)), false, false),
211    }
212  }
213}
214
215impl fmt::Display for PermissionState {
216  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217    match self {
218      PermissionState::Granted => f.pad("granted"),
219      PermissionState::GrantedPartial => f.pad("granted-partial"),
220      PermissionState::Prompt => f.pad("prompt"),
221      PermissionState::Denied => f.pad("denied"),
222    }
223  }
224}
225
226#[derive(Clone, Debug, Eq, PartialEq)]
227pub struct UnitPermission {
228  pub name: &'static str,
229  pub description: &'static str,
230  pub state: PermissionState,
231  pub prompt: bool,
232}
233
234impl UnitPermission {
235  pub fn query(&self) -> PermissionState {
236    self.state
237  }
238
239  pub fn request(&mut self) -> PermissionState {
240    if self.state == PermissionState::Prompt {
241      if PromptResponse::Allow
242        == permission_prompt(
243          &format!("access to {}", self.description),
244          self.name,
245          Some("Deno.permissions.query()"),
246          false,
247        )
248      {
249        self.state = PermissionState::Granted;
250      } else {
251        self.state = PermissionState::Denied;
252      }
253    }
254    self.state
255  }
256
257  pub fn revoke(&mut self) -> PermissionState {
258    if self.state == PermissionState::Granted {
259      self.state = PermissionState::Prompt;
260    }
261    self.state
262  }
263
264  pub fn check(&mut self) -> Result<(), PermissionDeniedError> {
265    let (result, prompted, _is_allow_all) =
266      self.state.check(self.name, None, None, self.prompt);
267    if prompted {
268      if result.is_ok() {
269        self.state = PermissionState::Granted;
270      } else {
271        self.state = PermissionState::Denied;
272      }
273    }
274    result
275  }
276
277  fn create_child_permissions(
278    &mut self,
279    flag: ChildUnitPermissionArg,
280  ) -> Result<Self, ChildPermissionError> {
281    let mut perm = self.clone();
282    match flag {
283      ChildUnitPermissionArg::Inherit => {
284        // copy
285      }
286      ChildUnitPermissionArg::Granted => {
287        if self.check().is_err() {
288          return Err(ChildPermissionError::Escalation);
289        }
290        perm.state = PermissionState::Granted;
291      }
292      ChildUnitPermissionArg::NotGranted => {
293        perm.state = PermissionState::Prompt;
294      }
295    }
296    if self.state == PermissionState::Denied {
297      perm.state = PermissionState::Denied;
298    }
299    Ok(perm)
300  }
301}
302
303/// A normalized environment variable name. On Windows this will
304/// be uppercase and on other platforms it will stay as-is.
305#[derive(Clone, Eq, PartialEq, Hash, Debug)]
306pub struct EnvVarName {
307  inner: String,
308}
309
310impl EnvVarName {
311  pub fn new(env: impl AsRef<str>) -> Self {
312    Self {
313      inner: if cfg!(windows) {
314        env.as_ref().to_uppercase()
315      } else {
316        env.as_ref().to_string()
317      },
318    }
319  }
320}
321
322impl AsRef<str> for EnvVarName {
323  fn as_ref(&self) -> &str {
324    self.inner.as_str()
325  }
326}
327
328pub trait QueryDescriptor: Debug {
329  type AllowDesc: Debug + Eq + Clone + Hash;
330  type DenyDesc: Debug + Eq + Clone + Hash;
331
332  fn flag_name() -> &'static str;
333  fn display_name(&self) -> Cow<str>;
334
335  fn from_allow(allow: &Self::AllowDesc) -> Self;
336
337  fn as_allow(&self) -> Option<Self::AllowDesc>;
338  fn as_deny(&self) -> Self::DenyDesc;
339
340  /// Generic check function to check this descriptor against a `UnaryPermission`.
341  fn check_in_permission(
342    &self,
343    perm: &mut UnaryPermission<Self>,
344    api_name: Option<&str>,
345  ) -> Result<(), PermissionDeniedError>;
346
347  fn matches_allow(&self, other: &Self::AllowDesc) -> bool;
348  fn matches_deny(&self, other: &Self::DenyDesc) -> bool;
349
350  /// Gets if this query descriptor should revoke the provided allow descriptor.
351  fn revokes(&self, other: &Self::AllowDesc) -> bool;
352  fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool;
353  fn overlaps_deny(&self, other: &Self::DenyDesc) -> bool;
354}
355
356fn format_display_name(display_name: Cow<str>) -> Cow<str> {
357  if display_name.starts_with('<') && display_name.ends_with('>') {
358    display_name
359  } else {
360    Cow::Owned(format!("\"{}\"", display_name))
361  }
362}
363
364#[derive(Debug, Eq, PartialEq)]
365pub struct UnaryPermission<TQuery: QueryDescriptor + ?Sized> {
366  granted_global: bool,
367  granted_list: HashSet<TQuery::AllowDesc>,
368  flag_denied_global: bool,
369  flag_denied_list: HashSet<TQuery::DenyDesc>,
370  prompt_denied_global: bool,
371  prompt_denied_list: HashSet<TQuery::DenyDesc>,
372  prompt: bool,
373}
374
375impl<TQuery: QueryDescriptor> Default for UnaryPermission<TQuery> {
376  fn default() -> Self {
377    UnaryPermission {
378      granted_global: Default::default(),
379      granted_list: Default::default(),
380      flag_denied_global: Default::default(),
381      flag_denied_list: Default::default(),
382      prompt_denied_global: Default::default(),
383      prompt_denied_list: Default::default(),
384      prompt: Default::default(),
385    }
386  }
387}
388
389impl<TQuery: QueryDescriptor> Clone for UnaryPermission<TQuery> {
390  fn clone(&self) -> Self {
391    Self {
392      granted_global: self.granted_global,
393      granted_list: self.granted_list.clone(),
394      flag_denied_global: self.flag_denied_global,
395      flag_denied_list: self.flag_denied_list.clone(),
396      prompt_denied_global: self.prompt_denied_global,
397      prompt_denied_list: self.prompt_denied_list.clone(),
398      prompt: self.prompt,
399    }
400  }
401}
402
403impl<TQuery: QueryDescriptor> UnaryPermission<TQuery> {
404  pub fn allow_all() -> Self {
405    Self {
406      granted_global: true,
407      ..Default::default()
408    }
409  }
410
411  pub fn is_allow_all(&self) -> bool {
412    self.granted_global
413      && self.flag_denied_list.is_empty()
414      && self.prompt_denied_list.is_empty()
415  }
416
417  pub fn check_all_api(
418    &mut self,
419    api_name: Option<&str>,
420  ) -> Result<(), PermissionDeniedError> {
421    skip_check_if_is_permission_fully_granted!(self);
422    self.check_desc(None, false, api_name)
423  }
424
425  fn check_desc(
426    &mut self,
427    desc: Option<&TQuery>,
428    assert_non_partial: bool,
429    api_name: Option<&str>,
430  ) -> Result<(), PermissionDeniedError> {
431    let (result, prompted, is_allow_all) = self
432      .query_desc(desc, AllowPartial::from(!assert_non_partial))
433      .check2(
434        TQuery::flag_name(),
435        api_name,
436        || desc.map(|d| format_display_name(d.display_name()).into_owned()),
437        self.prompt,
438      );
439    if prompted {
440      if result.is_ok() {
441        if is_allow_all {
442          self.insert_granted(None);
443        } else {
444          self.insert_granted(desc);
445        }
446      } else {
447        self.insert_prompt_denied(desc.map(|d| d.as_deny()));
448      }
449    }
450    result
451  }
452
453  fn query_desc(
454    &self,
455    desc: Option<&TQuery>,
456    allow_partial: AllowPartial,
457  ) -> PermissionState {
458    if self.is_flag_denied(desc) || self.is_prompt_denied(desc) {
459      PermissionState::Denied
460    } else if self.is_granted(desc) {
461      match allow_partial {
462        AllowPartial::TreatAsGranted => PermissionState::Granted,
463        AllowPartial::TreatAsDenied => {
464          if self.is_partial_flag_denied(desc) {
465            PermissionState::Denied
466          } else {
467            PermissionState::Granted
468          }
469        }
470        AllowPartial::TreatAsPartialGranted => {
471          if self.is_partial_flag_denied(desc) {
472            PermissionState::GrantedPartial
473          } else {
474            PermissionState::Granted
475          }
476        }
477      }
478    } else if matches!(allow_partial, AllowPartial::TreatAsDenied)
479      && self.is_partial_flag_denied(desc)
480    {
481      PermissionState::Denied
482    } else {
483      PermissionState::Prompt
484    }
485  }
486
487  fn request_desc(&mut self, desc: Option<&TQuery>) -> PermissionState {
488    let state = self.query_desc(desc, AllowPartial::TreatAsPartialGranted);
489    if state == PermissionState::Granted {
490      self.insert_granted(desc);
491      return state;
492    }
493    if state != PermissionState::Prompt {
494      return state;
495    }
496    if !self.prompt {
497      return PermissionState::Denied;
498    }
499    let maybe_formatted_display_name =
500      desc.map(|d| format_display_name(d.display_name()));
501    let message = StringBuilder::<String>::build(|builder| {
502      builder.append(TQuery::flag_name());
503      builder.append(" access");
504      if let Some(display_name) = &maybe_formatted_display_name {
505        builder.append(" to ");
506        builder.append(display_name)
507      }
508    })
509    .unwrap();
510    match permission_prompt(
511      &message,
512      TQuery::flag_name(),
513      Some("Deno.permissions.request()"),
514      true,
515    ) {
516      PromptResponse::Allow => {
517        self.insert_granted(desc);
518        PermissionState::Granted
519      }
520      PromptResponse::Deny => {
521        self.insert_prompt_denied(desc.map(|d| d.as_deny()));
522        PermissionState::Denied
523      }
524      PromptResponse::AllowAll => {
525        self.insert_granted(None);
526        PermissionState::Granted
527      }
528    }
529  }
530
531  fn revoke_desc(&mut self, desc: Option<&TQuery>) -> PermissionState {
532    match desc {
533      Some(desc) => {
534        self.granted_list.retain(|v| !desc.revokes(v));
535      }
536      None => {
537        self.granted_global = false;
538        // Revoke global is a special case where the entire granted list is
539        // cleared. It's inconsistent with the granular case where only
540        // descriptors stronger than the revoked one are purged.
541        self.granted_list.clear();
542      }
543    }
544    self.query_desc(desc, AllowPartial::TreatAsPartialGranted)
545  }
546
547  fn is_granted(&self, query: Option<&TQuery>) -> bool {
548    match query {
549      Some(query) => {
550        self.granted_global
551          || self.granted_list.iter().any(|v| query.matches_allow(v))
552      }
553      None => self.granted_global,
554    }
555  }
556
557  fn is_flag_denied(&self, query: Option<&TQuery>) -> bool {
558    match query {
559      Some(query) => {
560        self.flag_denied_global
561          || self.flag_denied_list.iter().any(|v| query.matches_deny(v))
562      }
563      None => self.flag_denied_global,
564    }
565  }
566
567  fn is_prompt_denied(&self, query: Option<&TQuery>) -> bool {
568    match query {
569      Some(query) => self
570        .prompt_denied_list
571        .iter()
572        .any(|v| query.stronger_than_deny(v)),
573      None => self.prompt_denied_global || !self.prompt_denied_list.is_empty(),
574    }
575  }
576
577  fn is_partial_flag_denied(&self, query: Option<&TQuery>) -> bool {
578    match query {
579      None => !self.flag_denied_list.is_empty(),
580      Some(query) => {
581        self.flag_denied_list.iter().any(|v| query.overlaps_deny(v))
582      }
583    }
584  }
585
586  fn insert_granted(&mut self, query: Option<&TQuery>) -> bool {
587    let desc = match query.map(|q| q.as_allow()) {
588      Some(Some(allow_desc)) => Some(allow_desc),
589      Some(None) => {
590        // the user was prompted for this descriptor in order to not
591        // expose anything about the system to the program, but the
592        // descriptor wasn't valid so no permission was raised
593        return false;
594      }
595      None => None,
596    };
597    Self::list_insert(desc, &mut self.granted_global, &mut self.granted_list);
598    true
599  }
600
601  fn insert_prompt_denied(&mut self, desc: Option<TQuery::DenyDesc>) {
602    Self::list_insert(
603      desc,
604      &mut self.prompt_denied_global,
605      &mut self.prompt_denied_list,
606    );
607  }
608
609  fn list_insert<T: Hash + Eq>(
610    desc: Option<T>,
611    list_global: &mut bool,
612    list: &mut HashSet<T>,
613  ) {
614    match desc {
615      Some(desc) => {
616        list.insert(desc);
617      }
618      None => *list_global = true,
619    }
620  }
621
622  fn create_child_permissions<E>(
623    &mut self,
624    flag: ChildUnaryPermissionArg,
625    parse: impl Fn(&str) -> Result<Option<TQuery::AllowDesc>, E>,
626  ) -> Result<UnaryPermission<TQuery>, ChildPermissionError>
627  where
628    ChildPermissionError: From<E>,
629  {
630    let mut perms = Self::default();
631
632    match flag {
633      ChildUnaryPermissionArg::Inherit => {
634        perms.clone_from(self);
635      }
636      ChildUnaryPermissionArg::Granted => {
637        if self.check_all_api(None).is_err() {
638          return Err(ChildPermissionError::Escalation);
639        }
640        perms.granted_global = true;
641      }
642      ChildUnaryPermissionArg::NotGranted => {}
643      ChildUnaryPermissionArg::GrantedList(granted_list) => {
644        perms.granted_list = granted_list
645          .iter()
646          .filter_map(|i| parse(i).transpose())
647          .collect::<Result<_, E>>()?;
648        if !perms.granted_list.iter().all(|desc| {
649          TQuery::from_allow(desc)
650            .check_in_permission(self, None)
651            .is_ok()
652        }) {
653          return Err(ChildPermissionError::Escalation);
654        }
655      }
656    }
657    perms.flag_denied_global = self.flag_denied_global;
658    perms.prompt_denied_global = self.prompt_denied_global;
659    perms.prompt = self.prompt;
660    perms.flag_denied_list.clone_from(&self.flag_denied_list);
661    perms
662      .prompt_denied_list
663      .clone_from(&self.prompt_denied_list);
664
665    Ok(perms)
666  }
667}
668
669#[derive(Clone, Eq, PartialEq, Hash, Debug)]
670pub struct PathQueryDescriptor {
671  pub requested: String,
672  pub resolved: PathBuf,
673}
674
675impl PathQueryDescriptor {
676  pub fn into_ffi(self) -> FfiQueryDescriptor {
677    FfiQueryDescriptor(self)
678  }
679
680  pub fn into_read(self) -> ReadQueryDescriptor {
681    ReadQueryDescriptor(self)
682  }
683
684  pub fn into_write(self) -> WriteQueryDescriptor {
685    WriteQueryDescriptor(self)
686  }
687}
688
689#[derive(Clone, Eq, PartialEq, Hash, Debug)]
690pub struct ReadQueryDescriptor(pub PathQueryDescriptor);
691
692impl QueryDescriptor for ReadQueryDescriptor {
693  type AllowDesc = ReadDescriptor;
694  type DenyDesc = ReadDescriptor;
695
696  fn flag_name() -> &'static str {
697    "read"
698  }
699
700  fn display_name(&self) -> Cow<str> {
701    Cow::Borrowed(self.0.requested.as_str())
702  }
703
704  fn from_allow(allow: &Self::AllowDesc) -> Self {
705    PathQueryDescriptor {
706      requested: allow.0.to_string_lossy().into_owned(),
707      resolved: allow.0.clone(),
708    }
709    .into_read()
710  }
711
712  fn as_allow(&self) -> Option<Self::AllowDesc> {
713    Some(ReadDescriptor(self.0.resolved.clone()))
714  }
715
716  fn as_deny(&self) -> Self::DenyDesc {
717    ReadDescriptor(self.0.resolved.clone())
718  }
719
720  fn check_in_permission(
721    &self,
722    perm: &mut UnaryPermission<Self>,
723    api_name: Option<&str>,
724  ) -> Result<(), PermissionDeniedError> {
725    skip_check_if_is_permission_fully_granted!(perm);
726    perm.check_desc(Some(self), true, api_name)
727  }
728
729  fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
730    self.0.resolved.starts_with(&other.0)
731  }
732
733  fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
734    self.0.resolved.starts_with(&other.0)
735  }
736
737  fn revokes(&self, other: &Self::AllowDesc) -> bool {
738    self.matches_allow(other)
739  }
740
741  fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
742    other.0.starts_with(&self.0.resolved)
743  }
744
745  fn overlaps_deny(&self, other: &Self::DenyDesc) -> bool {
746    self.stronger_than_deny(other)
747  }
748}
749
750#[derive(Clone, Eq, PartialEq, Hash, Debug)]
751pub struct ReadDescriptor(pub PathBuf);
752
753#[derive(Clone, Eq, PartialEq, Hash, Debug)]
754pub struct WriteQueryDescriptor(pub PathQueryDescriptor);
755
756impl QueryDescriptor for WriteQueryDescriptor {
757  type AllowDesc = WriteDescriptor;
758  type DenyDesc = WriteDescriptor;
759
760  fn flag_name() -> &'static str {
761    "write"
762  }
763
764  fn display_name(&self) -> Cow<str> {
765    Cow::Borrowed(&self.0.requested)
766  }
767
768  fn from_allow(allow: &Self::AllowDesc) -> Self {
769    WriteQueryDescriptor(PathQueryDescriptor {
770      requested: allow.0.to_string_lossy().into_owned(),
771      resolved: allow.0.clone(),
772    })
773  }
774
775  fn as_allow(&self) -> Option<Self::AllowDesc> {
776    Some(WriteDescriptor(self.0.resolved.clone()))
777  }
778
779  fn as_deny(&self) -> Self::DenyDesc {
780    WriteDescriptor(self.0.resolved.clone())
781  }
782
783  fn check_in_permission(
784    &self,
785    perm: &mut UnaryPermission<Self>,
786    api_name: Option<&str>,
787  ) -> Result<(), PermissionDeniedError> {
788    skip_check_if_is_permission_fully_granted!(perm);
789    perm.check_desc(Some(self), true, api_name)
790  }
791
792  fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
793    self.0.resolved.starts_with(&other.0)
794  }
795
796  fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
797    self.0.resolved.starts_with(&other.0)
798  }
799
800  fn revokes(&self, other: &Self::AllowDesc) -> bool {
801    self.matches_allow(other)
802  }
803
804  fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
805    other.0.starts_with(&self.0.resolved)
806  }
807
808  fn overlaps_deny(&self, other: &Self::DenyDesc) -> bool {
809    self.stronger_than_deny(other)
810  }
811}
812
813#[derive(Clone, Eq, PartialEq, Hash, Debug)]
814pub struct WriteDescriptor(pub PathBuf);
815
816#[derive(Clone, Eq, PartialEq, Hash, Debug)]
817pub enum Host {
818  Fqdn(FQDN),
819  Ip(IpAddr),
820  Vsock(u32),
821}
822
823#[derive(Debug, thiserror::Error, deno_error::JsError)]
824#[class(uri)]
825pub enum HostParseError {
826  #[error("invalid IPv6 address: '{0}'")]
827  InvalidIpv6(String),
828  #[error("invalid host: '{0}'")]
829  InvalidHost(String),
830  #[error("invalid empty host: '{0}'")]
831  InvalidEmptyHost(String),
832  #[error("invalid host '{host}': {error}")]
833  Fqdn {
834    #[source]
835    error: fqdn::Error,
836    host: String,
837  },
838}
839
840impl Host {
841  fn parse(s: &str) -> Result<Self, HostParseError> {
842    if s.starts_with('[') && s.ends_with(']') {
843      let ip = s[1..s.len() - 1]
844        .parse::<Ipv6Addr>()
845        .map_err(|_| HostParseError::InvalidIpv6(s.to_string()))?;
846      return Ok(Host::Ip(IpAddr::V6(ip)));
847    }
848    let (without_trailing_dot, has_trailing_dot) =
849      s.strip_suffix('.').map_or((s, false), |s| (s, true));
850    if let Ok(ip) = without_trailing_dot.parse::<IpAddr>() {
851      if has_trailing_dot {
852        return Err(HostParseError::InvalidHost(
853          without_trailing_dot.to_string(),
854        ));
855      }
856      Ok(Host::Ip(ip))
857    } else {
858      let lower = if s.chars().all(|c| c.is_ascii_lowercase()) {
859        Cow::Borrowed(s)
860      } else {
861        Cow::Owned(s.to_ascii_lowercase())
862      };
863      let fqdn = {
864        use std::str::FromStr;
865        FQDN::from_str(&lower).map_err(|e| HostParseError::Fqdn {
866          error: e,
867          host: s.to_string(),
868        })?
869      };
870      if fqdn.is_root() {
871        return Err(HostParseError::InvalidEmptyHost(s.to_string()));
872      }
873      Ok(Host::Fqdn(fqdn))
874    }
875  }
876
877  #[cfg(test)]
878  #[track_caller]
879  fn must_parse(s: &str) -> Self {
880    Self::parse(s).unwrap()
881  }
882}
883
884#[derive(Clone, Eq, PartialEq, Hash, Debug)]
885pub struct NetDescriptor(pub Host, pub Option<u32>);
886
887impl QueryDescriptor for NetDescriptor {
888  type AllowDesc = NetDescriptor;
889  type DenyDesc = NetDescriptor;
890
891  fn flag_name() -> &'static str {
892    "net"
893  }
894
895  fn display_name(&self) -> Cow<str> {
896    Cow::from(format!("{}", self))
897  }
898
899  fn from_allow(allow: &Self::AllowDesc) -> Self {
900    allow.clone()
901  }
902
903  fn as_allow(&self) -> Option<Self::AllowDesc> {
904    Some(self.clone())
905  }
906
907  fn as_deny(&self) -> Self::DenyDesc {
908    self.clone()
909  }
910
911  fn check_in_permission(
912    &self,
913    perm: &mut UnaryPermission<Self>,
914    api_name: Option<&str>,
915  ) -> Result<(), PermissionDeniedError> {
916    skip_check_if_is_permission_fully_granted!(perm);
917    perm.check_desc(Some(self), false, api_name)
918  }
919
920  fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
921    self.0 == other.0 && (other.1.is_none() || self.1 == other.1)
922  }
923
924  fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
925    self.0 == other.0 && (other.1.is_none() || self.1 == other.1)
926  }
927
928  fn revokes(&self, other: &Self::AllowDesc) -> bool {
929    self.matches_allow(other)
930  }
931
932  fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
933    self.matches_deny(other)
934  }
935
936  fn overlaps_deny(&self, _other: &Self::DenyDesc) -> bool {
937    false
938  }
939}
940
941#[derive(Debug, thiserror::Error)]
942pub enum NetDescriptorParseError {
943  #[error("invalid value '{0}': URLs are not supported, only domains and ips")]
944  Url(String),
945  #[error("invalid IPv6 address in '{hostname}': '{ip}'")]
946  InvalidIpv6 { hostname: String, ip: String },
947  #[error("invalid port in '{hostname}': '{port}'")]
948  InvalidPort { hostname: String, port: String },
949  #[error("invalid host: '{0}'")]
950  InvalidHost(String),
951  #[error("invalid empty port in '{0}'")]
952  EmptyPort(String),
953  #[error("ipv6 addresses must be enclosed in square brackets: '{0}'")]
954  Ipv6MissingSquareBrackets(String),
955  #[error("{0}")]
956  Host(#[from] HostParseError),
957  #[error("invalid vsock: '{0}'")]
958  InvalidVsock(String),
959}
960
961#[derive(Debug, thiserror::Error, deno_error::JsError)]
962pub enum NetDescriptorFromUrlParseError {
963  #[class(type)]
964  #[error("Missing host in url: '{0}'")]
965  MissingHost(Url),
966  #[class(inherit)]
967  #[error("{0}")]
968  Host(#[from] HostParseError),
969}
970
971impl NetDescriptor {
972  pub fn parse(hostname: &str) -> Result<Self, NetDescriptorParseError> {
973    #[cfg(unix)]
974    if let Some(vsock) = hostname.strip_prefix("vsock:") {
975      let mut split = vsock.split(':');
976      let Some(cid) = split.next().and_then(|c| {
977        if c == "-1" {
978          Some(u32::MAX)
979        } else {
980          c.parse().ok()
981        }
982      }) else {
983        return Err(NetDescriptorParseError::InvalidVsock(hostname.into()));
984      };
985      let Some(port) = split.next().and_then(|p| p.parse().ok()) else {
986        return Err(NetDescriptorParseError::InvalidVsock(hostname.into()));
987      };
988      return Ok(NetDescriptor(Host::Vsock(cid), Some(port)));
989    }
990
991    if hostname.starts_with("http://") || hostname.starts_with("https://") {
992      return Err(NetDescriptorParseError::Url(hostname.to_string()));
993    }
994
995    // If this is a IPv6 address enclosed in square brackets, parse it as such.
996    if hostname.starts_with('[') {
997      if let Some((ip, after)) = hostname.split_once(']') {
998        let ip = ip[1..].parse::<Ipv6Addr>().map_err(|_| {
999          NetDescriptorParseError::InvalidIpv6 {
1000            hostname: hostname.to_string(),
1001            ip: ip.to_string(),
1002          }
1003        })?;
1004        let port = if let Some(port) = after.strip_prefix(':') {
1005          let port = port.parse::<u16>().map_err(|_| {
1006            NetDescriptorParseError::InvalidPort {
1007              hostname: hostname.to_string(),
1008              port: port.to_string(),
1009            }
1010          })?;
1011          Some(port)
1012        } else if after.is_empty() {
1013          None
1014        } else {
1015          return Err(NetDescriptorParseError::InvalidHost(
1016            hostname.to_string(),
1017          ));
1018        };
1019        return Ok(NetDescriptor(
1020          Host::Ip(IpAddr::V6(ip)),
1021          port.map(Into::into),
1022        ));
1023      } else {
1024        return Err(NetDescriptorParseError::InvalidHost(hostname.to_string()));
1025      }
1026    }
1027
1028    // Otherwise it is an IPv4 address or a FQDN with an optional port.
1029    let (host, port) = match hostname.split_once(':') {
1030      Some((_, "")) => {
1031        return Err(NetDescriptorParseError::EmptyPort(hostname.to_string()));
1032      }
1033      Some((host, port)) => (host, port),
1034      None => (hostname, ""),
1035    };
1036    let host = Host::parse(host)?;
1037
1038    let port = if port.is_empty() {
1039      None
1040    } else {
1041      let port = port.parse::<u16>().map_err(|_| {
1042        // If the user forgot to enclose an IPv6 address in square brackets, we
1043        // should give them a hint. There are always at least two colons in an
1044        // IPv6 address, so this heuristic finds likely a bare IPv6 address.
1045        if port.contains(':') {
1046          NetDescriptorParseError::Ipv6MissingSquareBrackets(
1047            hostname.to_string(),
1048          )
1049        } else {
1050          NetDescriptorParseError::InvalidPort {
1051            hostname: hostname.to_string(),
1052            port: port.to_string(),
1053          }
1054        }
1055      })?;
1056      Some(port)
1057    };
1058
1059    Ok(NetDescriptor(host, port.map(Into::into)))
1060  }
1061
1062  pub fn from_url(url: &Url) -> Result<Self, NetDescriptorFromUrlParseError> {
1063    let host = url.host_str().ok_or_else(|| {
1064      NetDescriptorFromUrlParseError::MissingHost(url.clone())
1065    })?;
1066    let host = Host::parse(host)?;
1067    let port = url.port_or_known_default();
1068    Ok(NetDescriptor(host, port.map(Into::into)))
1069  }
1070
1071  pub fn from_vsock(
1072    cid: u32,
1073    port: u32,
1074  ) -> Result<Self, NetDescriptorParseError> {
1075    Ok(NetDescriptor(Host::Vsock(cid), Some(port)))
1076  }
1077}
1078
1079impl fmt::Display for NetDescriptor {
1080  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1081    match &self.0 {
1082      Host::Fqdn(fqdn) => write!(f, "{fqdn}"),
1083      Host::Ip(IpAddr::V4(ip)) => write!(f, "{ip}"),
1084      Host::Ip(IpAddr::V6(ip)) => write!(f, "[{ip}]"),
1085      Host::Vsock(cid) => write!(f, "vsock:{cid}"),
1086    }?;
1087    if let Some(port) = self.1 {
1088      write!(f, ":{}", port)?;
1089    }
1090    Ok(())
1091  }
1092}
1093
1094#[derive(Clone, Eq, PartialEq, Hash, Debug)]
1095pub struct ImportDescriptor(NetDescriptor);
1096
1097impl QueryDescriptor for ImportDescriptor {
1098  type AllowDesc = ImportDescriptor;
1099  type DenyDesc = ImportDescriptor;
1100
1101  fn flag_name() -> &'static str {
1102    "import"
1103  }
1104
1105  fn display_name(&self) -> Cow<str> {
1106    self.0.display_name()
1107  }
1108
1109  fn from_allow(allow: &Self::AllowDesc) -> Self {
1110    Self(NetDescriptor::from_allow(&allow.0))
1111  }
1112
1113  fn as_allow(&self) -> Option<Self::AllowDesc> {
1114    self.0.as_allow().map(ImportDescriptor)
1115  }
1116
1117  fn as_deny(&self) -> Self::DenyDesc {
1118    Self(self.0.as_deny())
1119  }
1120
1121  fn check_in_permission(
1122    &self,
1123    perm: &mut UnaryPermission<Self>,
1124    api_name: Option<&str>,
1125  ) -> Result<(), PermissionDeniedError> {
1126    skip_check_if_is_permission_fully_granted!(perm);
1127    perm.check_desc(Some(self), false, api_name)
1128  }
1129
1130  fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
1131    self.0.matches_allow(&other.0)
1132  }
1133
1134  fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
1135    self.0.matches_deny(&other.0)
1136  }
1137
1138  fn revokes(&self, other: &Self::AllowDesc) -> bool {
1139    self.0.revokes(&other.0)
1140  }
1141
1142  fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
1143    self.0.stronger_than_deny(&other.0)
1144  }
1145
1146  fn overlaps_deny(&self, other: &Self::DenyDesc) -> bool {
1147    self.0.overlaps_deny(&other.0)
1148  }
1149}
1150
1151impl ImportDescriptor {
1152  pub fn parse(specifier: &str) -> Result<Self, NetDescriptorParseError> {
1153    Ok(ImportDescriptor(NetDescriptor::parse(specifier)?))
1154  }
1155
1156  pub fn from_url(url: &Url) -> Result<Self, NetDescriptorFromUrlParseError> {
1157    Ok(ImportDescriptor(NetDescriptor::from_url(url)?))
1158  }
1159}
1160
1161#[derive(Debug, thiserror::Error)]
1162#[error("Empty env not allowed")]
1163pub struct EnvDescriptorParseError;
1164
1165#[derive(Clone, Eq, PartialEq, Hash, Debug)]
1166pub enum EnvDescriptor {
1167  Name(EnvVarName),
1168  PrefixPattern(EnvVarName),
1169}
1170
1171impl EnvDescriptor {
1172  pub fn new(env: impl AsRef<str>) -> Self {
1173    if let Some(prefix_pattern) = env.as_ref().strip_suffix('*') {
1174      Self::PrefixPattern(EnvVarName::new(prefix_pattern))
1175    } else {
1176      Self::Name(EnvVarName::new(env))
1177    }
1178  }
1179}
1180
1181#[derive(Clone, Eq, PartialEq, Hash, Debug)]
1182enum EnvQueryDescriptorInner {
1183  Name(EnvVarName),
1184  PrefixPattern(EnvVarName),
1185}
1186
1187#[derive(Clone, Eq, PartialEq, Hash, Debug)]
1188pub struct EnvQueryDescriptor(EnvQueryDescriptorInner);
1189
1190impl EnvQueryDescriptor {
1191  pub fn new(env: impl AsRef<str>) -> Self {
1192    Self(EnvQueryDescriptorInner::Name(EnvVarName::new(env)))
1193  }
1194}
1195
1196impl QueryDescriptor for EnvQueryDescriptor {
1197  type AllowDesc = EnvDescriptor;
1198  type DenyDesc = EnvDescriptor;
1199
1200  fn flag_name() -> &'static str {
1201    "env"
1202  }
1203
1204  fn display_name(&self) -> Cow<str> {
1205    Cow::from(match &self.0 {
1206      EnvQueryDescriptorInner::Name(env_var_name) => env_var_name.as_ref(),
1207      EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1208        env_var_name.as_ref()
1209      }
1210    })
1211  }
1212
1213  fn from_allow(allow: &Self::AllowDesc) -> Self {
1214    match allow {
1215      Self::AllowDesc::Name(s) => {
1216        Self(EnvQueryDescriptorInner::Name(s.clone()))
1217      }
1218      Self::AllowDesc::PrefixPattern(s) => {
1219        Self(EnvQueryDescriptorInner::PrefixPattern(s.clone()))
1220      }
1221    }
1222  }
1223
1224  fn as_allow(&self) -> Option<Self::AllowDesc> {
1225    Some(match &self.0 {
1226      EnvQueryDescriptorInner::Name(env_var_name) => {
1227        Self::AllowDesc::Name(env_var_name.clone())
1228      }
1229      EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1230        Self::AllowDesc::PrefixPattern(env_var_name.clone())
1231      }
1232    })
1233  }
1234
1235  fn as_deny(&self) -> Self::DenyDesc {
1236    match &self.0 {
1237      EnvQueryDescriptorInner::Name(env_var_name) => {
1238        Self::DenyDesc::Name(env_var_name.clone())
1239      }
1240      EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1241        Self::DenyDesc::PrefixPattern(env_var_name.clone())
1242      }
1243    }
1244  }
1245
1246  fn check_in_permission(
1247    &self,
1248    perm: &mut UnaryPermission<Self>,
1249    api_name: Option<&str>,
1250  ) -> Result<(), PermissionDeniedError> {
1251    skip_check_if_is_permission_fully_granted!(perm);
1252    perm.check_desc(Some(self), false, api_name)
1253  }
1254
1255  fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
1256    match other {
1257      Self::AllowDesc::Name(n) => match &self.0 {
1258        EnvQueryDescriptorInner::Name(env_var_name) => n == env_var_name,
1259        EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1260          env_var_name.as_ref().starts_with(n.as_ref())
1261        }
1262      },
1263      Self::AllowDesc::PrefixPattern(p) => match &self.0 {
1264        EnvQueryDescriptorInner::Name(env_var_name) => {
1265          env_var_name.as_ref().starts_with(p.as_ref())
1266        }
1267        EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1268          env_var_name.as_ref().starts_with(p.as_ref())
1269        }
1270      },
1271    }
1272  }
1273
1274  fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
1275    match other {
1276      Self::AllowDesc::Name(n) => match &self.0 {
1277        EnvQueryDescriptorInner::Name(env_var_name) => n == env_var_name,
1278        EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1279          env_var_name.as_ref().starts_with(n.as_ref())
1280        }
1281      },
1282      Self::AllowDesc::PrefixPattern(p) => match &self.0 {
1283        EnvQueryDescriptorInner::Name(env_var_name) => {
1284          env_var_name.as_ref().starts_with(p.as_ref())
1285        }
1286        EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1287          p == env_var_name
1288        }
1289      },
1290    }
1291  }
1292
1293  fn revokes(&self, other: &Self::AllowDesc) -> bool {
1294    match other {
1295      Self::AllowDesc::Name(n) => match &self.0 {
1296        EnvQueryDescriptorInner::Name(env_var_name) => n == env_var_name,
1297        EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1298          env_var_name.as_ref().starts_with(n.as_ref())
1299        }
1300      },
1301      Self::AllowDesc::PrefixPattern(p) => match &self.0 {
1302        EnvQueryDescriptorInner::Name(env_var_name) => {
1303          env_var_name.as_ref().starts_with(p.as_ref())
1304        }
1305        EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1306          p == env_var_name
1307        }
1308      },
1309    }
1310  }
1311
1312  fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
1313    match other {
1314      Self::AllowDesc::Name(n) => match &self.0 {
1315        EnvQueryDescriptorInner::Name(env_var_name) => n == env_var_name,
1316        EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1317          env_var_name.as_ref().starts_with(n.as_ref())
1318        }
1319      },
1320      Self::AllowDesc::PrefixPattern(p) => match &self.0 {
1321        EnvQueryDescriptorInner::Name(env_var_name) => {
1322          env_var_name.as_ref().starts_with(p.as_ref())
1323        }
1324        EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1325          p == env_var_name
1326        }
1327      },
1328    }
1329  }
1330
1331  fn overlaps_deny(&self, _other: &Self::DenyDesc) -> bool {
1332    false
1333  }
1334}
1335
1336impl AsRef<str> for EnvQueryDescriptor {
1337  fn as_ref(&self) -> &str {
1338    match &self.0 {
1339      EnvQueryDescriptorInner::Name(env_var_name) => env_var_name.as_ref(),
1340      EnvQueryDescriptorInner::PrefixPattern(env_var_name) => {
1341        env_var_name.as_ref()
1342      }
1343    }
1344  }
1345}
1346
1347#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
1348pub enum RunQueryDescriptor {
1349  Path {
1350    requested: String,
1351    resolved: PathBuf,
1352  },
1353  /// This variant won't actually grant permissions because the path of
1354  /// the executable is unresolved. It's mostly used so that prompts and
1355  /// everything works the same way as when the command is resolved,
1356  /// meaning that a script can't tell
1357  /// if a command is resolved or not based on how long something
1358  /// takes to ask for permissions.
1359  Name(String),
1360}
1361
1362#[derive(Debug, thiserror::Error, deno_error::JsError)]
1363pub enum PathResolveError {
1364  #[class(inherit)]
1365  #[error("failed resolving cwd: {0}")]
1366  CwdResolve(#[source] std::io::Error),
1367  #[class(generic)]
1368  #[error("Empty path is not allowed")]
1369  EmptyPath,
1370}
1371
1372impl RunQueryDescriptor {
1373  pub fn parse(
1374    requested: &str,
1375  ) -> Result<RunQueryDescriptor, PathResolveError> {
1376    if is_path(requested) {
1377      let path = PathBuf::from(requested);
1378      let resolved = if path.is_absolute() {
1379        normalize_path(path)
1380      } else {
1381        let cwd =
1382          std::env::current_dir().map_err(PathResolveError::CwdResolve)?;
1383        normalize_path(cwd.join(path))
1384      };
1385      Ok(RunQueryDescriptor::Path {
1386        requested: requested.to_string(),
1387        resolved,
1388      })
1389    } else {
1390      match which::which(requested) {
1391        Ok(resolved) => Ok(RunQueryDescriptor::Path {
1392          requested: requested.to_string(),
1393          resolved,
1394        }),
1395        Err(_) => Ok(RunQueryDescriptor::Name(requested.to_string())),
1396      }
1397    }
1398  }
1399}
1400
1401impl QueryDescriptor for RunQueryDescriptor {
1402  type AllowDesc = AllowRunDescriptor;
1403  type DenyDesc = DenyRunDescriptor;
1404
1405  fn flag_name() -> &'static str {
1406    "run"
1407  }
1408
1409  fn display_name(&self) -> Cow<str> {
1410    match self {
1411      RunQueryDescriptor::Path { requested, .. } => Cow::Borrowed(requested),
1412      RunQueryDescriptor::Name(name) => Cow::Borrowed(name),
1413    }
1414  }
1415
1416  fn from_allow(allow: &Self::AllowDesc) -> Self {
1417    RunQueryDescriptor::Path {
1418      requested: allow.0.to_string_lossy().into_owned(),
1419      resolved: allow.0.clone(),
1420    }
1421  }
1422
1423  fn as_allow(&self) -> Option<Self::AllowDesc> {
1424    match self {
1425      RunQueryDescriptor::Path { resolved, .. } => {
1426        Some(AllowRunDescriptor(resolved.clone()))
1427      }
1428      RunQueryDescriptor::Name(_) => None,
1429    }
1430  }
1431
1432  fn as_deny(&self) -> Self::DenyDesc {
1433    match self {
1434      RunQueryDescriptor::Path {
1435        resolved,
1436        requested,
1437      } => {
1438        if requested.contains('/')
1439          || (cfg!(windows) && requested.contains("\\"))
1440        {
1441          DenyRunDescriptor::Path(resolved.clone())
1442        } else {
1443          DenyRunDescriptor::Name(requested.clone())
1444        }
1445      }
1446      RunQueryDescriptor::Name(name) => DenyRunDescriptor::Name(name.clone()),
1447    }
1448  }
1449
1450  fn check_in_permission(
1451    &self,
1452    perm: &mut UnaryPermission<Self>,
1453    api_name: Option<&str>,
1454  ) -> Result<(), PermissionDeniedError> {
1455    skip_check_if_is_permission_fully_granted!(perm);
1456    perm.check_desc(Some(self), false, api_name)
1457  }
1458
1459  fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
1460    match self {
1461      RunQueryDescriptor::Path { resolved, .. } => *resolved == other.0,
1462      RunQueryDescriptor::Name(_) => false,
1463    }
1464  }
1465
1466  fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
1467    match other {
1468      DenyRunDescriptor::Name(deny_desc) => match self {
1469        RunQueryDescriptor::Path { resolved, .. } => {
1470          denies_run_name(deny_desc, resolved)
1471        }
1472        RunQueryDescriptor::Name(query) => query == deny_desc,
1473      },
1474      DenyRunDescriptor::Path(deny_desc) => match self {
1475        RunQueryDescriptor::Path { resolved, .. } => {
1476          resolved.starts_with(deny_desc)
1477        }
1478        RunQueryDescriptor::Name(query) => denies_run_name(query, deny_desc),
1479      },
1480    }
1481  }
1482
1483  fn revokes(&self, other: &Self::AllowDesc) -> bool {
1484    match self {
1485      RunQueryDescriptor::Path {
1486        resolved,
1487        requested,
1488      } => {
1489        if *resolved == other.0 {
1490          return true;
1491        }
1492        if is_path(requested) {
1493          false
1494        } else {
1495          denies_run_name(requested, &other.0)
1496        }
1497      }
1498      RunQueryDescriptor::Name(query) => denies_run_name(query, &other.0),
1499    }
1500  }
1501
1502  fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
1503    self.matches_deny(other)
1504  }
1505
1506  fn overlaps_deny(&self, _other: &Self::DenyDesc) -> bool {
1507    false
1508  }
1509}
1510
1511pub enum RunDescriptorArg {
1512  Name(String),
1513  Path(PathBuf),
1514}
1515
1516pub enum AllowRunDescriptorParseResult {
1517  /// An error occured getting the descriptor that should
1518  /// be surfaced as a warning when launching deno, but should
1519  /// be ignored when creating a worker.
1520  Unresolved(Box<which::Error>),
1521  Descriptor(AllowRunDescriptor),
1522}
1523
1524#[derive(Debug, thiserror::Error, deno_error::JsError)]
1525pub enum RunDescriptorParseError {
1526  #[class(generic)]
1527  #[error("{0}")]
1528  Which(#[from] which::Error),
1529  #[class(inherit)]
1530  #[error("{0}")]
1531  PathResolve(#[from] PathResolveError),
1532  #[class(generic)]
1533  #[error("Empty run query is not allowed")]
1534  EmptyRunQuery,
1535}
1536
1537#[derive(Debug, Clone, Hash, Eq, PartialEq)]
1538pub struct AllowRunDescriptor(pub PathBuf);
1539
1540impl AllowRunDescriptor {
1541  pub fn parse(
1542    text: &str,
1543    cwd: &Path,
1544  ) -> Result<AllowRunDescriptorParseResult, which::Error> {
1545    let is_path = is_path(text);
1546    // todo(dsherret): canonicalize in #25458
1547    let path = if is_path {
1548      resolve_from_known_cwd(Path::new(text), cwd)
1549    } else {
1550      match which::which_in(text, std::env::var_os("PATH"), cwd) {
1551        Ok(path) => path,
1552        Err(err) => match err {
1553          which::Error::CannotGetCurrentDirAndPathListEmpty => {
1554            return Err(err);
1555          }
1556          which::Error::CannotFindBinaryPath
1557          | which::Error::CannotCanonicalize => {
1558            return Ok(AllowRunDescriptorParseResult::Unresolved(Box::new(err)))
1559          }
1560        },
1561      }
1562    };
1563    Ok(AllowRunDescriptorParseResult::Descriptor(
1564      AllowRunDescriptor(path),
1565    ))
1566  }
1567}
1568
1569#[derive(Clone, Eq, PartialEq, Hash, Debug)]
1570pub enum DenyRunDescriptor {
1571  /// Warning: You may want to construct with `RunDescriptor::from()` for case
1572  /// handling.
1573  Name(String),
1574  /// Warning: You may want to construct with `RunDescriptor::from()` for case
1575  /// handling.
1576  Path(PathBuf),
1577}
1578
1579impl DenyRunDescriptor {
1580  pub fn parse(text: &str, cwd: &Path) -> Self {
1581    if text.contains('/') || cfg!(windows) && text.contains('\\') {
1582      let path = resolve_from_known_cwd(Path::new(&text), cwd);
1583      DenyRunDescriptor::Path(path)
1584    } else {
1585      DenyRunDescriptor::Name(text.to_string())
1586    }
1587  }
1588}
1589
1590fn is_path(text: &str) -> bool {
1591  if cfg!(windows) {
1592    text.contains('/') || text.contains('\\') || Path::new(text).is_absolute()
1593  } else {
1594    text.contains('/')
1595  }
1596}
1597
1598fn denies_run_name(name: &str, cmd_path: &Path) -> bool {
1599  let Some(file_stem) = cmd_path.file_stem() else {
1600    return false;
1601  };
1602  let Some(file_stem) = file_stem.to_str() else {
1603    return false;
1604  };
1605  if file_stem.len() < name.len() {
1606    return false;
1607  }
1608  let (prefix, suffix) = file_stem.split_at(name.len());
1609  if !prefix.eq_ignore_ascii_case(name) {
1610    return false;
1611  }
1612  // be broad and consider anything like `deno.something` as matching deny perms
1613  suffix.is_empty() || suffix.starts_with('.')
1614}
1615
1616#[derive(Debug, thiserror::Error, deno_error::JsError)]
1617pub enum SysDescriptorParseError {
1618  #[class(type)]
1619  #[error("unknown system info kind \"{0}\"")]
1620  InvalidKind(String),
1621  #[class(generic)]
1622  #[error("Empty sys not allowed")]
1623  Empty, // Error
1624}
1625
1626#[derive(Clone, Eq, PartialEq, Hash, Debug)]
1627pub struct SysDescriptor(String);
1628
1629impl SysDescriptor {
1630  pub fn parse(kind: String) -> Result<Self, SysDescriptorParseError> {
1631    match kind.as_str() {
1632      "hostname" | "inspector" | "osRelease" | "osUptime" | "loadavg"
1633      | "networkInterfaces" | "systemMemoryInfo" | "uid" | "gid" | "cpus"
1634      | "homedir" | "getegid" | "statfs" | "getPriority" | "setPriority"
1635      | "userInfo" => Ok(Self(kind)),
1636
1637      // the underlying permission check changed to `userInfo` to better match the API,
1638      // alias this to avoid breaking existing projects with `--allow-sys=username`
1639      "username" => Ok(Self("userInfo".into())),
1640      _ => Err(SysDescriptorParseError::InvalidKind(kind)),
1641    }
1642  }
1643
1644  pub fn into_string(self) -> String {
1645    self.0
1646  }
1647}
1648
1649impl QueryDescriptor for SysDescriptor {
1650  type AllowDesc = SysDescriptor;
1651  type DenyDesc = SysDescriptor;
1652
1653  fn flag_name() -> &'static str {
1654    "sys"
1655  }
1656
1657  fn display_name(&self) -> Cow<str> {
1658    Cow::from(self.0.to_string())
1659  }
1660
1661  fn from_allow(allow: &Self::AllowDesc) -> Self {
1662    allow.clone()
1663  }
1664
1665  fn as_allow(&self) -> Option<Self::AllowDesc> {
1666    Some(self.clone())
1667  }
1668
1669  fn as_deny(&self) -> Self::DenyDesc {
1670    self.clone()
1671  }
1672
1673  fn check_in_permission(
1674    &self,
1675    perm: &mut UnaryPermission<Self>,
1676    api_name: Option<&str>,
1677  ) -> Result<(), PermissionDeniedError> {
1678    skip_check_if_is_permission_fully_granted!(perm);
1679    perm.check_desc(Some(self), false, api_name)
1680  }
1681
1682  fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
1683    self == other
1684  }
1685
1686  fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
1687    self == other
1688  }
1689
1690  fn revokes(&self, other: &Self::AllowDesc) -> bool {
1691    self == other
1692  }
1693
1694  fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
1695    self == other
1696  }
1697
1698  fn overlaps_deny(&self, _other: &Self::DenyDesc) -> bool {
1699    false
1700  }
1701}
1702
1703#[derive(Clone, Eq, PartialEq, Hash, Debug)]
1704pub struct FfiQueryDescriptor(pub PathQueryDescriptor);
1705
1706impl QueryDescriptor for FfiQueryDescriptor {
1707  type AllowDesc = FfiDescriptor;
1708  type DenyDesc = FfiDescriptor;
1709
1710  fn flag_name() -> &'static str {
1711    "ffi"
1712  }
1713
1714  fn display_name(&self) -> Cow<str> {
1715    Cow::Borrowed(&self.0.requested)
1716  }
1717
1718  fn from_allow(allow: &Self::AllowDesc) -> Self {
1719    PathQueryDescriptor {
1720      requested: allow.0.to_string_lossy().into_owned(),
1721      resolved: allow.0.clone(),
1722    }
1723    .into_ffi()
1724  }
1725
1726  fn as_allow(&self) -> Option<Self::AllowDesc> {
1727    Some(FfiDescriptor(self.0.resolved.clone()))
1728  }
1729
1730  fn as_deny(&self) -> Self::DenyDesc {
1731    FfiDescriptor(self.0.resolved.clone())
1732  }
1733
1734  fn check_in_permission(
1735    &self,
1736    perm: &mut UnaryPermission<Self>,
1737    api_name: Option<&str>,
1738  ) -> Result<(), PermissionDeniedError> {
1739    skip_check_if_is_permission_fully_granted!(perm);
1740    perm.check_desc(Some(self), true, api_name)
1741  }
1742
1743  fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
1744    self.0.resolved.starts_with(&other.0)
1745  }
1746
1747  fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
1748    self.0.resolved.starts_with(&other.0)
1749  }
1750
1751  fn revokes(&self, other: &Self::AllowDesc) -> bool {
1752    self.matches_allow(other)
1753  }
1754
1755  fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
1756    other.0.starts_with(&self.0.resolved)
1757  }
1758
1759  fn overlaps_deny(&self, other: &Self::DenyDesc) -> bool {
1760    self.stronger_than_deny(other)
1761  }
1762}
1763
1764#[derive(Clone, Eq, PartialEq, Hash, Debug)]
1765pub struct FfiDescriptor(pub PathBuf);
1766
1767impl UnaryPermission<ReadQueryDescriptor> {
1768  pub fn query(&self, desc: Option<&ReadQueryDescriptor>) -> PermissionState {
1769    self.query_desc(desc, AllowPartial::TreatAsPartialGranted)
1770  }
1771
1772  pub fn request(
1773    &mut self,
1774    path: Option<&ReadQueryDescriptor>,
1775  ) -> PermissionState {
1776    self.request_desc(path)
1777  }
1778
1779  pub fn revoke(
1780    &mut self,
1781    desc: Option<&ReadQueryDescriptor>,
1782  ) -> PermissionState {
1783    self.revoke_desc(desc)
1784  }
1785
1786  pub fn check(
1787    &mut self,
1788    desc: &ReadQueryDescriptor,
1789    api_name: Option<&str>,
1790  ) -> Result<(), PermissionDeniedError> {
1791    skip_check_if_is_permission_fully_granted!(self);
1792    self.check_desc(Some(desc), true, api_name)
1793  }
1794
1795  #[inline]
1796  pub fn check_partial(
1797    &mut self,
1798    desc: &ReadQueryDescriptor,
1799    api_name: Option<&str>,
1800  ) -> Result<(), PermissionDeniedError> {
1801    skip_check_if_is_permission_fully_granted!(self);
1802    self.check_desc(Some(desc), false, api_name)
1803  }
1804
1805  pub fn check_all(
1806    &mut self,
1807    api_name: Option<&str>,
1808  ) -> Result<(), PermissionDeniedError> {
1809    skip_check_if_is_permission_fully_granted!(self);
1810    self.check_desc(None, false, api_name)
1811  }
1812}
1813
1814impl UnaryPermission<WriteQueryDescriptor> {
1815  pub fn query(&self, path: Option<&WriteQueryDescriptor>) -> PermissionState {
1816    self.query_desc(path, AllowPartial::TreatAsPartialGranted)
1817  }
1818
1819  pub fn request(
1820    &mut self,
1821    path: Option<&WriteQueryDescriptor>,
1822  ) -> PermissionState {
1823    self.request_desc(path)
1824  }
1825
1826  pub fn revoke(
1827    &mut self,
1828    path: Option<&WriteQueryDescriptor>,
1829  ) -> PermissionState {
1830    self.revoke_desc(path)
1831  }
1832
1833  pub fn check(
1834    &mut self,
1835    path: &WriteQueryDescriptor,
1836    api_name: Option<&str>,
1837  ) -> Result<(), PermissionDeniedError> {
1838    skip_check_if_is_permission_fully_granted!(self);
1839    self.check_desc(Some(path), true, api_name)
1840  }
1841
1842  #[inline]
1843  pub fn check_partial(
1844    &mut self,
1845    path: &WriteQueryDescriptor,
1846    api_name: Option<&str>,
1847  ) -> Result<(), PermissionDeniedError> {
1848    skip_check_if_is_permission_fully_granted!(self);
1849    self.check_desc(Some(path), false, api_name)
1850  }
1851
1852  pub fn check_all(
1853    &mut self,
1854    api_name: Option<&str>,
1855  ) -> Result<(), PermissionDeniedError> {
1856    skip_check_if_is_permission_fully_granted!(self);
1857    self.check_desc(None, false, api_name)
1858  }
1859}
1860
1861impl UnaryPermission<NetDescriptor> {
1862  pub fn query(&self, host: Option<&NetDescriptor>) -> PermissionState {
1863    self.query_desc(host, AllowPartial::TreatAsPartialGranted)
1864  }
1865
1866  pub fn request(&mut self, host: Option<&NetDescriptor>) -> PermissionState {
1867    self.request_desc(host)
1868  }
1869
1870  pub fn revoke(&mut self, host: Option<&NetDescriptor>) -> PermissionState {
1871    self.revoke_desc(host)
1872  }
1873
1874  pub fn check(
1875    &mut self,
1876    host: &NetDescriptor,
1877    api_name: Option<&str>,
1878  ) -> Result<(), PermissionDeniedError> {
1879    skip_check_if_is_permission_fully_granted!(self);
1880    self.check_desc(Some(host), false, api_name)
1881  }
1882
1883  pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
1884    skip_check_if_is_permission_fully_granted!(self);
1885    self.check_desc(None, false, None)
1886  }
1887}
1888
1889impl UnaryPermission<ImportDescriptor> {
1890  pub fn query(&self, host: Option<&ImportDescriptor>) -> PermissionState {
1891    self.query_desc(host, AllowPartial::TreatAsPartialGranted)
1892  }
1893
1894  pub fn request(
1895    &mut self,
1896    host: Option<&ImportDescriptor>,
1897  ) -> PermissionState {
1898    self.request_desc(host)
1899  }
1900
1901  pub fn revoke(&mut self, host: Option<&ImportDescriptor>) -> PermissionState {
1902    self.revoke_desc(host)
1903  }
1904
1905  pub fn check(
1906    &mut self,
1907    host: &ImportDescriptor,
1908    api_name: Option<&str>,
1909  ) -> Result<(), PermissionDeniedError> {
1910    skip_check_if_is_permission_fully_granted!(self);
1911    self.check_desc(Some(host), false, api_name)
1912  }
1913
1914  pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
1915    skip_check_if_is_permission_fully_granted!(self);
1916    self.check_desc(None, false, None)
1917  }
1918}
1919
1920impl UnaryPermission<EnvQueryDescriptor> {
1921  pub fn query(&self, env: Option<&str>) -> PermissionState {
1922    self.query_desc(
1923      env.map(EnvQueryDescriptor::new).as_ref(),
1924      AllowPartial::TreatAsPartialGranted,
1925    )
1926  }
1927
1928  pub fn request(&mut self, env: Option<&str>) -> PermissionState {
1929    self.request_desc(env.map(EnvQueryDescriptor::new).as_ref())
1930  }
1931
1932  pub fn revoke(&mut self, env: Option<&str>) -> PermissionState {
1933    self.revoke_desc(env.map(EnvQueryDescriptor::new).as_ref())
1934  }
1935
1936  pub fn check(
1937    &mut self,
1938    env: &str,
1939    api_name: Option<&str>,
1940  ) -> Result<(), PermissionDeniedError> {
1941    skip_check_if_is_permission_fully_granted!(self);
1942    self.check_desc(Some(&EnvQueryDescriptor::new(env)), false, api_name)
1943  }
1944
1945  pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
1946    skip_check_if_is_permission_fully_granted!(self);
1947    self.check_desc(None, false, None)
1948  }
1949}
1950
1951impl UnaryPermission<SysDescriptor> {
1952  pub fn query(&self, kind: Option<&SysDescriptor>) -> PermissionState {
1953    self.query_desc(kind, AllowPartial::TreatAsPartialGranted)
1954  }
1955
1956  pub fn request(&mut self, kind: Option<&SysDescriptor>) -> PermissionState {
1957    self.request_desc(kind)
1958  }
1959
1960  pub fn revoke(&mut self, kind: Option<&SysDescriptor>) -> PermissionState {
1961    self.revoke_desc(kind)
1962  }
1963
1964  pub fn check(
1965    &mut self,
1966    kind: &SysDescriptor,
1967    api_name: Option<&str>,
1968  ) -> Result<(), PermissionDeniedError> {
1969    skip_check_if_is_permission_fully_granted!(self);
1970    self.check_desc(Some(kind), false, api_name)
1971  }
1972
1973  pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
1974    skip_check_if_is_permission_fully_granted!(self);
1975    self.check_desc(None, false, None)
1976  }
1977}
1978
1979impl UnaryPermission<RunQueryDescriptor> {
1980  pub fn query(&self, cmd: Option<&RunQueryDescriptor>) -> PermissionState {
1981    self.query_desc(cmd, AllowPartial::TreatAsPartialGranted)
1982  }
1983
1984  pub fn request(
1985    &mut self,
1986    cmd: Option<&RunQueryDescriptor>,
1987  ) -> PermissionState {
1988    self.request_desc(cmd)
1989  }
1990
1991  pub fn revoke(
1992    &mut self,
1993    cmd: Option<&RunQueryDescriptor>,
1994  ) -> PermissionState {
1995    self.revoke_desc(cmd)
1996  }
1997
1998  pub fn check(
1999    &mut self,
2000    cmd: &RunQueryDescriptor,
2001    api_name: Option<&str>,
2002  ) -> Result<(), PermissionDeniedError> {
2003    self.check_desc(Some(cmd), false, api_name)
2004  }
2005
2006  pub fn check_all(
2007    &mut self,
2008    api_name: Option<&str>,
2009  ) -> Result<(), PermissionDeniedError> {
2010    self.check_desc(None, false, api_name)
2011  }
2012
2013  /// Queries without prompting
2014  pub fn query_all(&mut self, api_name: Option<&str>) -> bool {
2015    if self.is_allow_all() {
2016      return true;
2017    }
2018    let (result, _prompted, _is_allow_all) =
2019      self.query_desc(None, AllowPartial::TreatAsDenied).check2(
2020        RunQueryDescriptor::flag_name(),
2021        api_name,
2022        || None,
2023        /* prompt */ false,
2024      );
2025    result.is_ok()
2026  }
2027}
2028
2029impl UnaryPermission<FfiQueryDescriptor> {
2030  pub fn query(&self, path: Option<&FfiQueryDescriptor>) -> PermissionState {
2031    self.query_desc(path, AllowPartial::TreatAsPartialGranted)
2032  }
2033
2034  pub fn request(
2035    &mut self,
2036    path: Option<&FfiQueryDescriptor>,
2037  ) -> PermissionState {
2038    self.request_desc(path)
2039  }
2040
2041  pub fn revoke(
2042    &mut self,
2043    path: Option<&FfiQueryDescriptor>,
2044  ) -> PermissionState {
2045    self.revoke_desc(path)
2046  }
2047
2048  pub fn check(
2049    &mut self,
2050    path: &FfiQueryDescriptor,
2051    api_name: Option<&str>,
2052  ) -> Result<(), PermissionDeniedError> {
2053    skip_check_if_is_permission_fully_granted!(self);
2054    self.check_desc(Some(path), true, api_name)
2055  }
2056
2057  pub fn check_partial(
2058    &mut self,
2059    path: Option<&FfiQueryDescriptor>,
2060  ) -> Result<(), PermissionDeniedError> {
2061    skip_check_if_is_permission_fully_granted!(self);
2062    self.check_desc(path, false, None)
2063  }
2064
2065  pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
2066    skip_check_if_is_permission_fully_granted!(self);
2067    self.check_desc(None, false, Some("all"))
2068  }
2069}
2070
2071#[derive(Clone, Debug, Eq, PartialEq)]
2072pub struct Permissions {
2073  pub read: UnaryPermission<ReadQueryDescriptor>,
2074  pub write: UnaryPermission<WriteQueryDescriptor>,
2075  pub net: UnaryPermission<NetDescriptor>,
2076  pub env: UnaryPermission<EnvQueryDescriptor>,
2077  pub sys: UnaryPermission<SysDescriptor>,
2078  pub run: UnaryPermission<RunQueryDescriptor>,
2079  pub ffi: UnaryPermission<FfiQueryDescriptor>,
2080  pub import: UnaryPermission<ImportDescriptor>,
2081  pub all: UnitPermission,
2082}
2083
2084#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)]
2085pub struct PermissionsOptions {
2086  pub allow_all: bool,
2087  pub allow_env: Option<Vec<String>>,
2088  pub deny_env: Option<Vec<String>>,
2089  pub allow_net: Option<Vec<String>>,
2090  pub deny_net: Option<Vec<String>>,
2091  pub allow_ffi: Option<Vec<String>>,
2092  pub deny_ffi: Option<Vec<String>>,
2093  pub allow_read: Option<Vec<String>>,
2094  pub deny_read: Option<Vec<String>>,
2095  pub allow_run: Option<Vec<String>>,
2096  pub deny_run: Option<Vec<String>>,
2097  pub allow_sys: Option<Vec<String>>,
2098  pub deny_sys: Option<Vec<String>>,
2099  pub allow_write: Option<Vec<String>>,
2100  pub deny_write: Option<Vec<String>>,
2101  pub allow_import: Option<Vec<String>>,
2102  pub prompt: bool,
2103}
2104
2105#[derive(Debug, thiserror::Error)]
2106pub enum PermissionsFromOptionsError {
2107  #[error("{0}")]
2108  PathResolve(#[from] PathResolveError),
2109  #[error("{0}")]
2110  SysDescriptorParse(#[from] SysDescriptorParseError),
2111  #[error("{0}")]
2112  NetDescriptorParse(#[from] NetDescriptorParseError),
2113  #[error("{0}")]
2114  EnvDescriptorParse(#[from] EnvDescriptorParseError),
2115  #[error("{0}")]
2116  RunDescriptorParse(#[from] RunDescriptorParseError),
2117  #[error("Empty command name not allowed in --allow-run=...")]
2118  RunEmptyCommandName,
2119}
2120
2121impl Permissions {
2122  pub fn new_unary<TQuery>(
2123    allow_list: Option<HashSet<TQuery::AllowDesc>>,
2124    deny_list: Option<HashSet<TQuery::DenyDesc>>,
2125    prompt: bool,
2126  ) -> UnaryPermission<TQuery>
2127  where
2128    TQuery: QueryDescriptor,
2129  {
2130    UnaryPermission::<TQuery> {
2131      granted_global: global_from_option(allow_list.as_ref()),
2132      granted_list: allow_list.unwrap_or_default(),
2133      flag_denied_global: global_from_option(deny_list.as_ref()),
2134      flag_denied_list: deny_list.unwrap_or_default(),
2135      prompt,
2136      ..Default::default()
2137    }
2138  }
2139
2140  pub const fn new_all(allow_state: bool) -> UnitPermission {
2141    unit_permission_from_flag_bools(
2142      allow_state,
2143      false,
2144      "all",
2145      "all",
2146      false, // never prompt for all
2147    )
2148  }
2149
2150  pub fn from_options(
2151    parser: &dyn PermissionDescriptorParser,
2152    opts: &PermissionsOptions,
2153  ) -> Result<Self, PermissionsFromOptionsError> {
2154    fn resolve_allow_run(
2155      parser: &dyn PermissionDescriptorParser,
2156      allow_run: &[String],
2157    ) -> Result<HashSet<AllowRunDescriptor>, PermissionsFromOptionsError> {
2158      let mut new_allow_run = HashSet::with_capacity(allow_run.len());
2159      for unresolved in allow_run {
2160        if unresolved.is_empty() {
2161          return Err(PermissionsFromOptionsError::RunEmptyCommandName);
2162        }
2163        match parser.parse_allow_run_descriptor(unresolved)? {
2164          AllowRunDescriptorParseResult::Descriptor(descriptor) => {
2165            new_allow_run.insert(descriptor);
2166          }
2167          AllowRunDescriptorParseResult::Unresolved(err) => {
2168            log::info!(
2169              "{} Failed to resolve '{}' for allow-run: {}",
2170              colors::gray("Info"),
2171              unresolved,
2172              err
2173            );
2174          }
2175        }
2176      }
2177      Ok(new_allow_run)
2178    }
2179
2180    fn parse_maybe_vec<T: Eq + PartialEq + Hash, E>(
2181      items: Option<&[String]>,
2182      parse: impl Fn(&str) -> Result<T, E>,
2183    ) -> Result<Option<HashSet<T>>, PermissionsFromOptionsError>
2184    where
2185      PermissionsFromOptionsError: From<E>,
2186    {
2187      match items {
2188        Some(items) => Ok(Some(
2189          items
2190            .iter()
2191            .map(|item| parse(item))
2192            .collect::<Result<HashSet<_>, _>>()?,
2193        )),
2194        None => Ok(None),
2195      }
2196    }
2197
2198    let mut deny_write = parse_maybe_vec(opts.deny_write.as_deref(), |item| {
2199      parser.parse_write_descriptor(item)
2200    })?;
2201    let allow_run = opts
2202      .allow_run
2203      .as_ref()
2204      .and_then(|raw_allow_run| {
2205        match resolve_allow_run(parser, raw_allow_run) {
2206          Ok(resolved_allow_run) => {
2207            if resolved_allow_run.is_empty() && !raw_allow_run.is_empty() {
2208              None // convert to no permissions if now empty
2209            } else {
2210              Some(Ok(resolved_allow_run))
2211            }
2212          }
2213          Err(err) => Some(Err(err)),
2214        }
2215      })
2216      .transpose()?;
2217    // add the allow_run list to deny_write
2218    if let Some(allow_run_vec) = &allow_run {
2219      if !allow_run_vec.is_empty() {
2220        let deny_write = deny_write.get_or_insert_with(Default::default);
2221        deny_write.extend(
2222          allow_run_vec
2223            .iter()
2224            .map(|item| WriteDescriptor(item.0.clone())),
2225        );
2226      }
2227    }
2228
2229    Ok(Self {
2230      read: Permissions::new_unary(
2231        parse_maybe_vec(opts.allow_read.as_deref(), |item| {
2232          parser.parse_read_descriptor(item)
2233        })?,
2234        parse_maybe_vec(opts.deny_read.as_deref(), |item| {
2235          parser.parse_read_descriptor(item)
2236        })?,
2237        opts.prompt,
2238      ),
2239      write: Permissions::new_unary(
2240        parse_maybe_vec(opts.allow_write.as_deref(), |item| {
2241          parser.parse_write_descriptor(item)
2242        })?,
2243        deny_write,
2244        opts.prompt,
2245      ),
2246      net: Permissions::new_unary(
2247        parse_maybe_vec(opts.allow_net.as_deref(), |item| {
2248          parser.parse_net_descriptor(item)
2249        })?,
2250        parse_maybe_vec(opts.deny_net.as_deref(), |item| {
2251          parser.parse_net_descriptor(item)
2252        })?,
2253        opts.prompt,
2254      ),
2255      env: Permissions::new_unary(
2256        parse_maybe_vec(opts.allow_env.as_deref(), |item| {
2257          parser.parse_env_descriptor(item)
2258        })?,
2259        parse_maybe_vec(opts.deny_env.as_deref(), |text| {
2260          parser.parse_env_descriptor(text)
2261        })?,
2262        opts.prompt,
2263      ),
2264      sys: Permissions::new_unary(
2265        parse_maybe_vec(opts.allow_sys.as_deref(), |text| {
2266          parser.parse_sys_descriptor(text)
2267        })?,
2268        parse_maybe_vec(opts.deny_sys.as_deref(), |text| {
2269          parser.parse_sys_descriptor(text)
2270        })?,
2271        opts.prompt,
2272      ),
2273      run: Permissions::new_unary(
2274        allow_run,
2275        parse_maybe_vec(opts.deny_run.as_deref(), |text| {
2276          parser.parse_deny_run_descriptor(text)
2277        })?,
2278        opts.prompt,
2279      ),
2280      ffi: Permissions::new_unary(
2281        parse_maybe_vec(opts.allow_ffi.as_deref(), |text| {
2282          parser.parse_ffi_descriptor(text)
2283        })?,
2284        parse_maybe_vec(opts.deny_ffi.as_deref(), |text| {
2285          parser.parse_ffi_descriptor(text)
2286        })?,
2287        opts.prompt,
2288      ),
2289      import: Permissions::new_unary(
2290        parse_maybe_vec(opts.allow_import.as_deref(), |item| {
2291          parser.parse_import_descriptor(item)
2292        })?,
2293        None,
2294        opts.prompt,
2295      ),
2296      all: Permissions::new_all(opts.allow_all),
2297    })
2298  }
2299
2300  /// Create a set of permissions that explicitly allow everything.
2301  pub fn allow_all() -> Self {
2302    Self {
2303      read: UnaryPermission::allow_all(),
2304      write: UnaryPermission::allow_all(),
2305      net: UnaryPermission::allow_all(),
2306      env: UnaryPermission::allow_all(),
2307      sys: UnaryPermission::allow_all(),
2308      run: UnaryPermission::allow_all(),
2309      ffi: UnaryPermission::allow_all(),
2310      import: UnaryPermission::allow_all(),
2311      all: Permissions::new_all(true),
2312    }
2313  }
2314
2315  /// Create a set of permissions that enable nothing, but will allow prompting.
2316  pub fn none_with_prompt() -> Self {
2317    Self::none(true)
2318  }
2319
2320  /// Create a set of permissions that enable nothing, and will not allow prompting.
2321  pub fn none_without_prompt() -> Self {
2322    Self::none(false)
2323  }
2324
2325  fn none(prompt: bool) -> Self {
2326    Self {
2327      read: Permissions::new_unary(None, None, prompt),
2328      write: Permissions::new_unary(None, None, prompt),
2329      net: Permissions::new_unary(None, None, prompt),
2330      env: Permissions::new_unary(None, None, prompt),
2331      sys: Permissions::new_unary(None, None, prompt),
2332      run: Permissions::new_unary(None, None, prompt),
2333      ffi: Permissions::new_unary(None, None, prompt),
2334      import: Permissions::new_unary(None, None, prompt),
2335      all: Permissions::new_all(false),
2336    }
2337  }
2338}
2339
2340#[derive(Debug, Clone, Copy, Eq, PartialEq)]
2341pub enum CheckSpecifierKind {
2342  Static,
2343  Dynamic,
2344}
2345
2346#[derive(Debug, thiserror::Error, deno_error::JsError)]
2347pub enum ChildPermissionError {
2348  #[class("NotCapable")]
2349  #[error("Can't escalate parent thread permissions")]
2350  Escalation,
2351  #[class(inherit)]
2352  #[error("{0}")]
2353  PathResolve(#[from] PathResolveError),
2354  #[class(uri)]
2355  #[error("{0}")]
2356  NetDescriptorParse(#[from] NetDescriptorParseError),
2357  #[class(generic)]
2358  #[error("{0}")]
2359  EnvDescriptorParse(#[from] EnvDescriptorParseError),
2360  #[class(inherit)]
2361  #[error("{0}")]
2362  SysDescriptorParse(#[from] SysDescriptorParseError),
2363  #[class(inherit)]
2364  #[error("{0}")]
2365  RunDescriptorParse(#[from] RunDescriptorParseError),
2366}
2367
2368#[derive(Debug, thiserror::Error, deno_error::JsError)]
2369pub enum PermissionCheckError {
2370  #[class("NotCapable")]
2371  #[error(transparent)]
2372  PermissionDenied(#[from] PermissionDeniedError),
2373  #[class(uri)]
2374  #[error("Invalid file path.\n  Specifier: {0}")]
2375  InvalidFilePath(Url),
2376  #[class(inherit)]
2377  #[error(transparent)]
2378  NetDescriptorForUrlParse(#[from] NetDescriptorFromUrlParseError),
2379  #[class(inherit)]
2380  #[error(transparent)]
2381  SysDescriptorParse(#[from] SysDescriptorParseError),
2382  #[class(inherit)]
2383  #[error(transparent)]
2384  PathResolve(#[from] PathResolveError),
2385  #[class(uri)]
2386  #[error(transparent)]
2387  HostParse(#[from] HostParseError),
2388  #[class("NotCapable")]
2389  #[error("Permission denied {0}")]
2390  NotCapable(&'static str),
2391}
2392
2393/// Wrapper struct for `Permissions` that can be shared across threads.
2394///
2395/// We need a way to have internal mutability for permissions as they might get
2396/// passed to a future that will prompt the user for permission (and in such
2397/// case might need to be mutated). Also for the Web Worker API we need a way
2398/// to send permissions to a new thread.
2399#[derive(Clone, Debug)]
2400pub struct PermissionsContainer {
2401  descriptor_parser: Arc<dyn PermissionDescriptorParser>,
2402  inner: Arc<Mutex<Permissions>>,
2403}
2404
2405impl PermissionsContainer {
2406  pub fn new(
2407    descriptor_parser: Arc<dyn PermissionDescriptorParser>,
2408    perms: Permissions,
2409  ) -> Self {
2410    Self {
2411      descriptor_parser,
2412      inner: Arc::new(Mutex::new(perms)),
2413    }
2414  }
2415
2416  pub fn allow_all(
2417    descriptor_parser: Arc<dyn PermissionDescriptorParser>,
2418  ) -> Self {
2419    Self::new(descriptor_parser, Permissions::allow_all())
2420  }
2421
2422  pub fn create_child_permissions(
2423    &self,
2424    child_permissions_arg: ChildPermissionsArg,
2425  ) -> Result<PermissionsContainer, ChildPermissionError> {
2426    fn is_granted_unary(arg: &ChildUnaryPermissionArg) -> bool {
2427      match arg {
2428        ChildUnaryPermissionArg::Inherit | ChildUnaryPermissionArg::Granted => {
2429          true
2430        }
2431        ChildUnaryPermissionArg::NotGranted
2432        | ChildUnaryPermissionArg::GrantedList(_) => false,
2433      }
2434    }
2435
2436    let mut worker_perms = Permissions::none_without_prompt();
2437
2438    let mut inner = self.inner.lock();
2439    worker_perms.all = inner
2440      .all
2441      .create_child_permissions(ChildUnitPermissionArg::Inherit)?;
2442
2443    // downgrade the `worker_perms.all` based on the other values
2444    if worker_perms.all.query() == PermissionState::Granted {
2445      let unary_perms = [
2446        &child_permissions_arg.read,
2447        &child_permissions_arg.write,
2448        &child_permissions_arg.net,
2449        &child_permissions_arg.import,
2450        &child_permissions_arg.env,
2451        &child_permissions_arg.sys,
2452        &child_permissions_arg.run,
2453        &child_permissions_arg.ffi,
2454      ];
2455      let allow_all = unary_perms.into_iter().all(is_granted_unary);
2456      if !allow_all {
2457        worker_perms.all.revoke();
2458      }
2459    }
2460
2461    // WARNING: When adding a permission here, ensure it is handled
2462    // in the worker_perms.all block above
2463    worker_perms.read = inner.read.create_child_permissions(
2464      child_permissions_arg.read,
2465      |text| {
2466        Ok::<_, PathResolveError>(Some(
2467          self.descriptor_parser.parse_read_descriptor(text)?,
2468        ))
2469      },
2470    )?;
2471    worker_perms.write = inner.write.create_child_permissions(
2472      child_permissions_arg.write,
2473      |text| {
2474        Ok::<_, PathResolveError>(Some(
2475          self.descriptor_parser.parse_write_descriptor(text)?,
2476        ))
2477      },
2478    )?;
2479    worker_perms.import = inner.import.create_child_permissions(
2480      child_permissions_arg.import,
2481      |text| {
2482        Ok::<_, NetDescriptorParseError>(Some(
2483          self.descriptor_parser.parse_import_descriptor(text)?,
2484        ))
2485      },
2486    )?;
2487    worker_perms.net = inner.net.create_child_permissions(
2488      child_permissions_arg.net,
2489      |text| {
2490        Ok::<_, NetDescriptorParseError>(Some(
2491          self.descriptor_parser.parse_net_descriptor(text)?,
2492        ))
2493      },
2494    )?;
2495    worker_perms.env = inner.env.create_child_permissions(
2496      child_permissions_arg.env,
2497      |text| {
2498        Ok::<_, EnvDescriptorParseError>(Some(
2499          self.descriptor_parser.parse_env_descriptor(text)?,
2500        ))
2501      },
2502    )?;
2503    worker_perms.sys = inner.sys.create_child_permissions(
2504      child_permissions_arg.sys,
2505      |text| {
2506        Ok::<_, SysDescriptorParseError>(Some(
2507          self.descriptor_parser.parse_sys_descriptor(text)?,
2508        ))
2509      },
2510    )?;
2511    worker_perms.run = inner.run.create_child_permissions(
2512      child_permissions_arg.run,
2513      |text| match self.descriptor_parser.parse_allow_run_descriptor(text)? {
2514        AllowRunDescriptorParseResult::Unresolved(_) => {
2515          Ok::<_, RunDescriptorParseError>(None)
2516        }
2517        AllowRunDescriptorParseResult::Descriptor(desc) => Ok(Some(desc)),
2518      },
2519    )?;
2520    worker_perms.ffi = inner.ffi.create_child_permissions(
2521      child_permissions_arg.ffi,
2522      |text| {
2523        Ok::<_, PathResolveError>(Some(
2524          self.descriptor_parser.parse_ffi_descriptor(text)?,
2525        ))
2526      },
2527    )?;
2528
2529    Ok(PermissionsContainer::new(
2530      self.descriptor_parser.clone(),
2531      worker_perms,
2532    ))
2533  }
2534
2535  #[inline(always)]
2536  pub fn check_specifier(
2537    &self,
2538    specifier: &ModuleSpecifier,
2539    kind: CheckSpecifierKind,
2540  ) -> Result<(), PermissionCheckError> {
2541    let mut inner = self.inner.lock();
2542    match specifier.scheme() {
2543      "file" => {
2544        if inner.read.is_allow_all() || kind == CheckSpecifierKind::Static {
2545          return Ok(());
2546        }
2547
2548        match url_to_file_path(specifier) {
2549          Ok(path) => inner
2550            .read
2551            .check(
2552              &PathQueryDescriptor {
2553                requested: path.to_string_lossy().into_owned(),
2554                resolved: path,
2555              }
2556              .into_read(),
2557              Some("import()"),
2558            )
2559            .map_err(PermissionCheckError::PermissionDenied),
2560          Err(_) => {
2561            Err(PermissionCheckError::InvalidFilePath(specifier.clone()))
2562          }
2563        }
2564      }
2565      "data" => Ok(()),
2566      "blob" => Ok(()),
2567      _ => {
2568        if inner.import.is_allow_all() {
2569          return Ok(()); // avoid allocation below
2570        }
2571
2572        let desc = self
2573          .descriptor_parser
2574          .parse_import_descriptor_from_url(specifier)?;
2575        inner.import.check(&desc, Some("import()"))?;
2576        Ok(())
2577      }
2578    }
2579  }
2580
2581  #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
2582  #[inline(always)]
2583  pub fn check_read(
2584    &self,
2585    path: &str,
2586    api_name: &str,
2587  ) -> Result<PathBuf, PermissionCheckError> {
2588    self.check_read_with_api_name(path, Some(api_name))
2589  }
2590
2591  #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
2592  #[inline(always)]
2593  pub fn check_read_with_api_name(
2594    &self,
2595    path: &str,
2596    api_name: Option<&str>,
2597  ) -> Result<PathBuf, PermissionCheckError> {
2598    let mut inner = self.inner.lock();
2599    let inner = &mut inner.read;
2600    if inner.is_allow_all() {
2601      Ok(PathBuf::from(path))
2602    } else {
2603      let desc = self.descriptor_parser.parse_path_query(path)?.into_read();
2604      inner.check(&desc, api_name)?;
2605      Ok(desc.0.resolved)
2606    }
2607  }
2608
2609  #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
2610  #[inline(always)]
2611  pub fn check_read_path<'a>(
2612    &self,
2613    path: &'a Path,
2614    api_name: Option<&str>,
2615  ) -> Result<Cow<'a, Path>, PermissionCheckError> {
2616    let mut inner = self.inner.lock();
2617    let inner = &mut inner.read;
2618    if inner.is_allow_all() {
2619      Ok(Cow::Borrowed(path))
2620    } else {
2621      let desc = PathQueryDescriptor {
2622        requested: path.to_string_lossy().into_owned(),
2623        resolved: path.to_path_buf(),
2624      }
2625      .into_read();
2626      inner.check(&desc, api_name)?;
2627
2628      Ok(Cow::Owned(desc.0.resolved))
2629    }
2630  }
2631
2632  /// As `check_read()`, but permission error messages will anonymize the path
2633  /// by replacing it with the given `display`.
2634  #[inline(always)]
2635  pub fn check_read_blind(
2636    &self,
2637    path: &Path,
2638    display: &str,
2639    api_name: &str,
2640  ) -> Result<(), PermissionCheckError> {
2641    let mut inner = self.inner.lock();
2642    let inner = &mut inner.read;
2643    skip_check_if_is_permission_fully_granted!(inner);
2644    inner.check(
2645      &PathQueryDescriptor {
2646        requested: format!("<{}>", display),
2647        resolved: path.to_path_buf(),
2648      }
2649      .into_read(),
2650      Some(api_name),
2651    )?;
2652    Ok(())
2653  }
2654
2655  #[inline(always)]
2656  pub fn check_read_all(
2657    &self,
2658    api_name: &str,
2659  ) -> Result<(), PermissionCheckError> {
2660    self.inner.lock().read.check_all(Some(api_name))?;
2661    Ok(())
2662  }
2663
2664  #[inline(always)]
2665  pub fn query_read_all(&self) -> bool {
2666    self.inner.lock().read.query(None) == PermissionState::Granted
2667  }
2668
2669  #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
2670  #[inline(always)]
2671  pub fn check_write(
2672    &self,
2673    path: &str,
2674    api_name: &str,
2675  ) -> Result<PathBuf, PermissionCheckError> {
2676    self.check_write_with_api_name(path, Some(api_name))
2677  }
2678
2679  #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
2680  #[inline(always)]
2681  pub fn check_write_with_api_name(
2682    &self,
2683    path: &str,
2684    api_name: Option<&str>,
2685  ) -> Result<PathBuf, PermissionCheckError> {
2686    let mut inner = self.inner.lock();
2687    let inner = &mut inner.write;
2688    if inner.is_allow_all() {
2689      Ok(PathBuf::from(path))
2690    } else {
2691      let desc = self.descriptor_parser.parse_path_query(path)?.into_write();
2692      inner.check(&desc, api_name)?;
2693      Ok(desc.0.resolved)
2694    }
2695  }
2696
2697  #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
2698  #[inline(always)]
2699  pub fn check_write_path<'a>(
2700    &self,
2701    path: &'a Path,
2702    api_name: &str,
2703  ) -> Result<Cow<'a, Path>, PermissionCheckError> {
2704    let mut inner = self.inner.lock();
2705    let inner = &mut inner.write;
2706    if inner.is_allow_all() {
2707      Ok(Cow::Borrowed(path))
2708    } else {
2709      let desc = PathQueryDescriptor {
2710        requested: path.to_string_lossy().into_owned(),
2711        resolved: path.to_path_buf(),
2712      }
2713      .into_write();
2714      inner.check(&desc, Some(api_name))?;
2715      Ok(Cow::Owned(desc.0.resolved))
2716    }
2717  }
2718
2719  #[inline(always)]
2720  pub fn check_write_all(
2721    &self,
2722    api_name: &str,
2723  ) -> Result<(), PermissionCheckError> {
2724    self.inner.lock().write.check_all(Some(api_name))?;
2725    Ok(())
2726  }
2727
2728  /// As `check_write()`, but permission error messages will anonymize the path
2729  /// by replacing it with the given `display`.
2730  #[inline(always)]
2731  pub fn check_write_blind(
2732    &self,
2733    path: &Path,
2734    display: &str,
2735    api_name: &str,
2736  ) -> Result<(), PermissionCheckError> {
2737    let mut inner = self.inner.lock();
2738    let inner = &mut inner.write;
2739    skip_check_if_is_permission_fully_granted!(inner);
2740    inner.check(
2741      &PathQueryDescriptor {
2742        requested: format!("<{}>", display),
2743        resolved: path.to_path_buf(),
2744      }
2745      .into_write(),
2746      Some(api_name),
2747    )?;
2748    Ok(())
2749  }
2750
2751  #[inline(always)]
2752  pub fn check_write_partial(
2753    &self,
2754    path: &str,
2755    api_name: &str,
2756  ) -> Result<PathBuf, PermissionCheckError> {
2757    let mut inner = self.inner.lock();
2758    let inner = &mut inner.write;
2759    if inner.is_allow_all() {
2760      Ok(PathBuf::from(path))
2761    } else {
2762      let desc = self.descriptor_parser.parse_path_query(path)?.into_write();
2763      inner.check_partial(&desc, Some(api_name))?;
2764      Ok(desc.0.resolved)
2765    }
2766  }
2767
2768  #[inline(always)]
2769  pub fn check_run(
2770    &self,
2771    cmd: &RunQueryDescriptor,
2772    api_name: &str,
2773  ) -> Result<(), PermissionCheckError> {
2774    self.inner.lock().run.check(cmd, Some(api_name))?;
2775    Ok(())
2776  }
2777
2778  #[inline(always)]
2779  pub fn check_run_all(
2780    &mut self,
2781    api_name: &str,
2782  ) -> Result<(), PermissionCheckError> {
2783    self.inner.lock().run.check_all(Some(api_name))?;
2784    Ok(())
2785  }
2786
2787  #[inline(always)]
2788  pub fn query_run_all(&mut self, api_name: &str) -> bool {
2789    self.inner.lock().run.query_all(Some(api_name))
2790  }
2791
2792  #[inline(always)]
2793  pub fn check_sys(
2794    &self,
2795    kind: &str,
2796    api_name: &str,
2797  ) -> Result<(), PermissionCheckError> {
2798    self.inner.lock().sys.check(
2799      &self.descriptor_parser.parse_sys_descriptor(kind)?,
2800      Some(api_name),
2801    )?;
2802    Ok(())
2803  }
2804
2805  #[inline(always)]
2806  pub fn check_env(&self, var: &str) -> Result<(), PermissionCheckError> {
2807    self.inner.lock().env.check(var, None)?;
2808    Ok(())
2809  }
2810
2811  #[inline(always)]
2812  pub fn check_env_all(&self) -> Result<(), PermissionCheckError> {
2813    self.inner.lock().env.check_all()?;
2814    Ok(())
2815  }
2816
2817  #[inline(always)]
2818  pub fn check_sys_all(&self) -> Result<(), PermissionCheckError> {
2819    self.inner.lock().sys.check_all()?;
2820    Ok(())
2821  }
2822
2823  #[inline(always)]
2824  pub fn check_ffi_all(&self) -> Result<(), PermissionCheckError> {
2825    self.inner.lock().ffi.check_all()?;
2826    Ok(())
2827  }
2828
2829  /// This checks to see if the allow-all flag was passed, not whether all
2830  /// permissions are enabled!
2831  #[inline(always)]
2832  pub fn check_was_allow_all_flag_passed(
2833    &self,
2834  ) -> Result<(), PermissionCheckError> {
2835    self.inner.lock().all.check()?;
2836    Ok(())
2837  }
2838
2839  /// Checks special file access, returning the failed permission type if
2840  /// not successful.
2841  pub fn check_special_file(
2842    &self,
2843    path: &Path,
2844    _api_name: &str,
2845  ) -> Result<(), &'static str> {
2846    let error_all = |_| "all";
2847
2848    // Safe files with no major additional side-effects. While there's a small risk of someone
2849    // draining system entropy by just reading one of these files constantly, that's not really
2850    // something we worry about as they already have --allow-read to /dev.
2851    if cfg!(unix)
2852      && (path == OsStr::new("/dev/random")
2853        || path == OsStr::new("/dev/urandom")
2854        || path == OsStr::new("/dev/zero")
2855        || path == OsStr::new("/dev/null"))
2856    {
2857      return Ok(());
2858    }
2859
2860    /// We'll allow opening /proc/self/fd/{n} without additional permissions under the following conditions:
2861    ///
2862    /// 1. n > 2. This allows for opening bash-style redirections, but not stdio
2863    /// 2. the fd referred to by n is a pipe
2864    #[cfg(unix)]
2865    fn is_fd_file_is_pipe(path: &Path) -> bool {
2866      if let Some(fd) = path.file_name() {
2867        if let Ok(s) = std::str::from_utf8(fd.as_encoded_bytes()) {
2868          if let Ok(n) = s.parse::<i32>() {
2869            if n > 2 {
2870              // SAFETY: This is proper use of the stat syscall
2871              unsafe {
2872                let mut stat = std::mem::zeroed::<libc::stat>();
2873                if libc::fstat(n, &mut stat as _) == 0
2874                  && ((stat.st_mode & libc::S_IFMT) & libc::S_IFIFO) != 0
2875                {
2876                  return true;
2877                }
2878              };
2879            }
2880          }
2881        }
2882      }
2883      false
2884    }
2885
2886    // On unixy systems, we allow opening /dev/fd/XXX for valid FDs that
2887    // are pipes.
2888    #[cfg(unix)]
2889    if path.starts_with("/dev/fd") && is_fd_file_is_pipe(path) {
2890      return Ok(());
2891    }
2892
2893    if cfg!(target_os = "linux") {
2894      // On Linux, we also allow opening /proc/self/fd/XXX for valid FDs that
2895      // are pipes.
2896      #[cfg(unix)]
2897      if path.starts_with("/proc/self/fd") && is_fd_file_is_pipe(path) {
2898        return Ok(());
2899      }
2900      if path.starts_with("/dev")
2901        || path.starts_with("/proc")
2902        || path.starts_with("/sys")
2903      {
2904        if path.ends_with("/environ") {
2905          self.check_env_all().map_err(|_| "env")?;
2906        } else {
2907          self.check_was_allow_all_flag_passed().map_err(error_all)?;
2908        }
2909      }
2910    } else if cfg!(unix) {
2911      if path.starts_with("/dev") {
2912        self.check_was_allow_all_flag_passed().map_err(error_all)?;
2913      }
2914    } else if cfg!(target_os = "windows") {
2915      // \\.\nul is allowed
2916      let s = path.as_os_str().as_encoded_bytes();
2917      if s.eq_ignore_ascii_case(br#"\\.\nul"#) {
2918        return Ok(());
2919      }
2920
2921      fn is_normalized_windows_drive_path(path: &Path) -> bool {
2922        let s = path.as_os_str().as_encoded_bytes();
2923        // \\?\X:\
2924        if s.len() < 7 {
2925          false
2926        } else if s.starts_with(br#"\\?\"#) {
2927          s[4].is_ascii_alphabetic() && s[5] == b':' && s[6] == b'\\'
2928        } else {
2929          false
2930        }
2931      }
2932
2933      // If this is a normalized drive path, accept it
2934      if !is_normalized_windows_drive_path(path) {
2935        self.check_was_allow_all_flag_passed().map_err(error_all)?;
2936      }
2937    } else {
2938      unimplemented!()
2939    }
2940    Ok(())
2941  }
2942
2943  #[inline(always)]
2944  pub fn check_net_url(
2945    &mut self,
2946    url: &Url,
2947    api_name: &str,
2948  ) -> Result<(), PermissionCheckError> {
2949    let mut inner = self.inner.lock();
2950    if inner.net.is_allow_all() {
2951      return Ok(());
2952    }
2953    let desc = self.descriptor_parser.parse_net_descriptor_from_url(url)?;
2954    inner.net.check(&desc, Some(api_name))?;
2955    Ok(())
2956  }
2957
2958  #[inline(always)]
2959  pub fn check_net<T: AsRef<str>>(
2960    &mut self,
2961    host: &(T, Option<u16>),
2962    api_name: &str,
2963  ) -> Result<(), PermissionCheckError> {
2964    let mut inner = self.inner.lock();
2965    let inner = &mut inner.net;
2966    skip_check_if_is_permission_fully_granted!(inner);
2967    let hostname = Host::parse(host.0.as_ref())?;
2968    let descriptor = NetDescriptor(hostname, host.1.map(Into::into));
2969    inner.check(&descriptor, Some(api_name))?;
2970    Ok(())
2971  }
2972
2973  #[inline(always)]
2974  pub fn check_net_vsock(
2975    &mut self,
2976    cid: u32,
2977    port: u32,
2978    api_name: &str,
2979  ) -> Result<(), PermissionCheckError> {
2980    let mut inner = self.inner.lock();
2981    if inner.net.is_allow_all() {
2982      return Ok(());
2983    }
2984    let desc = NetDescriptor(Host::Vsock(cid), Some(port));
2985    inner.net.check(&desc, Some(api_name))?;
2986    Ok(())
2987  }
2988
2989  #[inline(always)]
2990  pub fn check_ffi(
2991    &mut self,
2992    path: &str,
2993  ) -> Result<PathBuf, PermissionCheckError> {
2994    let mut inner = self.inner.lock();
2995    let inner = &mut inner.ffi;
2996    if inner.is_allow_all() {
2997      Ok(PathBuf::from(path))
2998    } else {
2999      let desc = self.descriptor_parser.parse_path_query(path)?.into_ffi();
3000      inner.check(&desc, None)?;
3001      Ok(desc.0.resolved)
3002    }
3003  }
3004
3005  #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
3006  #[inline(always)]
3007  pub fn check_ffi_partial_no_path(
3008    &mut self,
3009  ) -> Result<(), PermissionCheckError> {
3010    let mut inner = self.inner.lock();
3011    let inner = &mut inner.ffi;
3012    if !inner.is_allow_all() {
3013      inner.check_partial(None)?;
3014    }
3015    Ok(())
3016  }
3017
3018  #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
3019  #[inline(always)]
3020  pub fn check_ffi_partial_with_path(
3021    &mut self,
3022    path: &str,
3023  ) -> Result<PathBuf, PermissionCheckError> {
3024    let mut inner = self.inner.lock();
3025    let inner = &mut inner.ffi;
3026    if inner.is_allow_all() {
3027      Ok(PathBuf::from(path))
3028    } else {
3029      let desc = self.descriptor_parser.parse_path_query(path)?.into_ffi();
3030      inner.check_partial(Some(&desc))?;
3031      Ok(desc.0.resolved)
3032    }
3033  }
3034
3035  // query
3036
3037  #[inline(always)]
3038  pub fn query_read(
3039    &self,
3040    path: Option<&str>,
3041  ) -> Result<PermissionState, PathResolveError> {
3042    let inner = self.inner.lock();
3043    let permission = &inner.read;
3044    if permission.is_allow_all() {
3045      return Ok(PermissionState::Granted);
3046    }
3047    Ok(
3048      permission.query(
3049        path
3050          .map(|path| {
3051            Ok::<_, PathResolveError>(
3052              self.descriptor_parser.parse_path_query(path)?.into_read(),
3053            )
3054          })
3055          .transpose()?
3056          .as_ref(),
3057      ),
3058    )
3059  }
3060
3061  #[inline(always)]
3062  pub fn query_write(
3063    &self,
3064    path: Option<&str>,
3065  ) -> Result<PermissionState, PathResolveError> {
3066    let inner = self.inner.lock();
3067    let permission = &inner.write;
3068    if permission.is_allow_all() {
3069      return Ok(PermissionState::Granted);
3070    }
3071    Ok(
3072      permission.query(
3073        path
3074          .map(|path| {
3075            Ok::<_, PathResolveError>(
3076              self.descriptor_parser.parse_path_query(path)?.into_write(),
3077            )
3078          })
3079          .transpose()?
3080          .as_ref(),
3081      ),
3082    )
3083  }
3084
3085  #[inline(always)]
3086  pub fn query_net(
3087    &self,
3088    host: Option<&str>,
3089  ) -> Result<PermissionState, NetDescriptorParseError> {
3090    let inner = self.inner.lock();
3091    let permission = &inner.net;
3092    if permission.is_allow_all() {
3093      return Ok(PermissionState::Granted);
3094    }
3095    Ok(
3096      permission.query(
3097        match host {
3098          None => None,
3099          Some(h) => Some(self.descriptor_parser.parse_net_descriptor(h)?),
3100        }
3101        .as_ref(),
3102      ),
3103    )
3104  }
3105
3106  #[inline(always)]
3107  pub fn query_env(&self, var: Option<&str>) -> PermissionState {
3108    let inner = self.inner.lock();
3109    let permission = &inner.env;
3110    if permission.is_allow_all() {
3111      return PermissionState::Granted;
3112    }
3113    permission.query(var)
3114  }
3115
3116  #[inline(always)]
3117  pub fn query_sys(
3118    &self,
3119    kind: Option<&str>,
3120  ) -> Result<PermissionState, SysDescriptorParseError> {
3121    let inner = self.inner.lock();
3122    let permission = &inner.sys;
3123    if permission.is_allow_all() {
3124      return Ok(PermissionState::Granted);
3125    }
3126    Ok(
3127      permission.query(
3128        kind
3129          .map(|kind| self.descriptor_parser.parse_sys_descriptor(kind))
3130          .transpose()?
3131          .as_ref(),
3132      ),
3133    )
3134  }
3135
3136  #[inline(always)]
3137  pub fn query_run(
3138    &self,
3139    cmd: Option<&str>,
3140  ) -> Result<PermissionState, RunDescriptorParseError> {
3141    let inner = self.inner.lock();
3142    let permission = &inner.run;
3143    if permission.is_allow_all() {
3144      return Ok(PermissionState::Granted);
3145    }
3146    Ok(
3147      permission.query(
3148        cmd
3149          .map(|request| self.descriptor_parser.parse_run_query(request))
3150          .transpose()?
3151          .as_ref(),
3152      ),
3153    )
3154  }
3155
3156  #[inline(always)]
3157  pub fn query_ffi(
3158    &self,
3159    path: Option<&str>,
3160  ) -> Result<PermissionState, PathResolveError> {
3161    let inner = self.inner.lock();
3162    let permission = &inner.ffi;
3163    if permission.is_allow_all() {
3164      return Ok(PermissionState::Granted);
3165    }
3166    Ok(
3167      permission.query(
3168        path
3169          .map(|path| {
3170            Ok::<_, PathResolveError>(
3171              self.descriptor_parser.parse_path_query(path)?.into_ffi(),
3172            )
3173          })
3174          .transpose()?
3175          .as_ref(),
3176      ),
3177    )
3178  }
3179
3180  // revoke
3181
3182  #[inline(always)]
3183  pub fn revoke_read(
3184    &self,
3185    path: Option<&str>,
3186  ) -> Result<PermissionState, PathResolveError> {
3187    Ok(
3188      self.inner.lock().read.revoke(
3189        path
3190          .map(|path| {
3191            Ok::<_, PathResolveError>(
3192              self.descriptor_parser.parse_path_query(path)?.into_read(),
3193            )
3194          })
3195          .transpose()?
3196          .as_ref(),
3197      ),
3198    )
3199  }
3200
3201  #[inline(always)]
3202  pub fn revoke_write(
3203    &self,
3204    path: Option<&str>,
3205  ) -> Result<PermissionState, PathResolveError> {
3206    Ok(
3207      self.inner.lock().write.revoke(
3208        path
3209          .map(|path| {
3210            Ok::<_, PathResolveError>(
3211              self.descriptor_parser.parse_path_query(path)?.into_write(),
3212            )
3213          })
3214          .transpose()?
3215          .as_ref(),
3216      ),
3217    )
3218  }
3219
3220  #[inline(always)]
3221  pub fn revoke_net(
3222    &self,
3223    host: Option<&str>,
3224  ) -> Result<PermissionState, NetDescriptorParseError> {
3225    Ok(
3226      self.inner.lock().net.revoke(
3227        match host {
3228          None => None,
3229          Some(h) => Some(self.descriptor_parser.parse_net_descriptor(h)?),
3230        }
3231        .as_ref(),
3232      ),
3233    )
3234  }
3235
3236  #[inline(always)]
3237  pub fn revoke_env(&self, var: Option<&str>) -> PermissionState {
3238    self.inner.lock().env.revoke(var)
3239  }
3240
3241  #[inline(always)]
3242  pub fn revoke_sys(
3243    &self,
3244    kind: Option<&str>,
3245  ) -> Result<PermissionState, SysDescriptorParseError> {
3246    Ok(
3247      self.inner.lock().sys.revoke(
3248        kind
3249          .map(|kind| self.descriptor_parser.parse_sys_descriptor(kind))
3250          .transpose()?
3251          .as_ref(),
3252      ),
3253    )
3254  }
3255
3256  #[inline(always)]
3257  pub fn revoke_run(
3258    &self,
3259    cmd: Option<&str>,
3260  ) -> Result<PermissionState, RunDescriptorParseError> {
3261    Ok(
3262      self.inner.lock().run.revoke(
3263        cmd
3264          .map(|request| self.descriptor_parser.parse_run_query(request))
3265          .transpose()?
3266          .as_ref(),
3267      ),
3268    )
3269  }
3270
3271  #[inline(always)]
3272  pub fn revoke_ffi(
3273    &self,
3274    path: Option<&str>,
3275  ) -> Result<PermissionState, PathResolveError> {
3276    Ok(
3277      self.inner.lock().ffi.revoke(
3278        path
3279          .map(|path| {
3280            Ok::<_, PathResolveError>(
3281              self.descriptor_parser.parse_path_query(path)?.into_ffi(),
3282            )
3283          })
3284          .transpose()?
3285          .as_ref(),
3286      ),
3287    )
3288  }
3289
3290  // request
3291
3292  #[inline(always)]
3293  pub fn request_read(
3294    &self,
3295    path: Option<&str>,
3296  ) -> Result<PermissionState, PathResolveError> {
3297    Ok(
3298      self.inner.lock().read.request(
3299        path
3300          .map(|path| {
3301            Ok::<_, PathResolveError>(
3302              self.descriptor_parser.parse_path_query(path)?.into_read(),
3303            )
3304          })
3305          .transpose()?
3306          .as_ref(),
3307      ),
3308    )
3309  }
3310
3311  #[inline(always)]
3312  pub fn request_write(
3313    &self,
3314    path: Option<&str>,
3315  ) -> Result<PermissionState, PathResolveError> {
3316    Ok(
3317      self.inner.lock().write.request(
3318        path
3319          .map(|path| {
3320            Ok::<_, PathResolveError>(
3321              self.descriptor_parser.parse_path_query(path)?.into_write(),
3322            )
3323          })
3324          .transpose()?
3325          .as_ref(),
3326      ),
3327    )
3328  }
3329
3330  #[inline(always)]
3331  pub fn request_net(
3332    &self,
3333    host: Option<&str>,
3334  ) -> Result<PermissionState, NetDescriptorParseError> {
3335    Ok(
3336      self.inner.lock().net.request(
3337        match host {
3338          None => None,
3339          Some(h) => Some(self.descriptor_parser.parse_net_descriptor(h)?),
3340        }
3341        .as_ref(),
3342      ),
3343    )
3344  }
3345
3346  #[inline(always)]
3347  pub fn request_env(&self, var: Option<&str>) -> PermissionState {
3348    self.inner.lock().env.request(var)
3349  }
3350
3351  #[inline(always)]
3352  pub fn request_sys(
3353    &self,
3354    kind: Option<&str>,
3355  ) -> Result<PermissionState, SysDescriptorParseError> {
3356    Ok(
3357      self.inner.lock().sys.request(
3358        kind
3359          .map(|kind| self.descriptor_parser.parse_sys_descriptor(kind))
3360          .transpose()?
3361          .as_ref(),
3362      ),
3363    )
3364  }
3365
3366  #[inline(always)]
3367  pub fn request_run(
3368    &self,
3369    cmd: Option<&str>,
3370  ) -> Result<PermissionState, RunDescriptorParseError> {
3371    Ok(
3372      self.inner.lock().run.request(
3373        cmd
3374          .map(|request| self.descriptor_parser.parse_run_query(request))
3375          .transpose()?
3376          .as_ref(),
3377      ),
3378    )
3379  }
3380
3381  #[inline(always)]
3382  pub fn request_ffi(
3383    &self,
3384    path: Option<&str>,
3385  ) -> Result<PermissionState, PathResolveError> {
3386    Ok(
3387      self.inner.lock().ffi.request(
3388        path
3389          .map(|path| {
3390            Ok::<_, PathResolveError>(
3391              self.descriptor_parser.parse_path_query(path)?.into_ffi(),
3392            )
3393          })
3394          .transpose()?
3395          .as_ref(),
3396      ),
3397    )
3398  }
3399}
3400
3401const fn unit_permission_from_flag_bools(
3402  allow_flag: bool,
3403  deny_flag: bool,
3404  name: &'static str,
3405  description: &'static str,
3406  prompt: bool,
3407) -> UnitPermission {
3408  UnitPermission {
3409    name,
3410    description,
3411    state: if deny_flag {
3412      PermissionState::Denied
3413    } else if allow_flag {
3414      PermissionState::Granted
3415    } else {
3416      PermissionState::Prompt
3417    },
3418    prompt,
3419  }
3420}
3421
3422fn global_from_option<T>(flag: Option<&HashSet<T>>) -> bool {
3423  matches!(flag, Some(v) if v.is_empty())
3424}
3425
3426#[derive(Debug, Eq, PartialEq)]
3427pub enum ChildUnitPermissionArg {
3428  Inherit,
3429  Granted,
3430  NotGranted,
3431}
3432
3433impl<'de> Deserialize<'de> for ChildUnitPermissionArg {
3434  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3435  where
3436    D: Deserializer<'de>,
3437  {
3438    struct ChildUnitPermissionArgVisitor;
3439    impl de::Visitor<'_> for ChildUnitPermissionArgVisitor {
3440      type Value = ChildUnitPermissionArg;
3441
3442      fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
3443        formatter.write_str("\"inherit\" or boolean")
3444      }
3445
3446      fn visit_unit<E>(self) -> Result<ChildUnitPermissionArg, E>
3447      where
3448        E: de::Error,
3449      {
3450        Ok(ChildUnitPermissionArg::NotGranted)
3451      }
3452
3453      fn visit_str<E>(self, v: &str) -> Result<ChildUnitPermissionArg, E>
3454      where
3455        E: de::Error,
3456      {
3457        if v == "inherit" {
3458          Ok(ChildUnitPermissionArg::Inherit)
3459        } else {
3460          Err(de::Error::invalid_value(de::Unexpected::Str(v), &self))
3461        }
3462      }
3463
3464      fn visit_bool<E>(self, v: bool) -> Result<ChildUnitPermissionArg, E>
3465      where
3466        E: de::Error,
3467      {
3468        match v {
3469          true => Ok(ChildUnitPermissionArg::Granted),
3470          false => Ok(ChildUnitPermissionArg::NotGranted),
3471        }
3472      }
3473    }
3474    deserializer.deserialize_any(ChildUnitPermissionArgVisitor)
3475  }
3476}
3477
3478#[derive(Debug, Eq, PartialEq)]
3479pub enum ChildUnaryPermissionArg {
3480  Inherit,
3481  Granted,
3482  NotGranted,
3483  GrantedList(Vec<String>),
3484}
3485
3486impl<'de> Deserialize<'de> for ChildUnaryPermissionArg {
3487  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3488  where
3489    D: Deserializer<'de>,
3490  {
3491    struct ChildUnaryPermissionArgVisitor;
3492    impl<'de> de::Visitor<'de> for ChildUnaryPermissionArgVisitor {
3493      type Value = ChildUnaryPermissionArg;
3494
3495      fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
3496        formatter.write_str("\"inherit\" or boolean or string[]")
3497      }
3498
3499      fn visit_unit<E>(self) -> Result<ChildUnaryPermissionArg, E>
3500      where
3501        E: de::Error,
3502      {
3503        Ok(ChildUnaryPermissionArg::NotGranted)
3504      }
3505
3506      fn visit_str<E>(self, v: &str) -> Result<ChildUnaryPermissionArg, E>
3507      where
3508        E: de::Error,
3509      {
3510        if v == "inherit" {
3511          Ok(ChildUnaryPermissionArg::Inherit)
3512        } else {
3513          Err(de::Error::invalid_value(de::Unexpected::Str(v), &self))
3514        }
3515      }
3516
3517      fn visit_bool<E>(self, v: bool) -> Result<ChildUnaryPermissionArg, E>
3518      where
3519        E: de::Error,
3520      {
3521        match v {
3522          true => Ok(ChildUnaryPermissionArg::Granted),
3523          false => Ok(ChildUnaryPermissionArg::NotGranted),
3524        }
3525      }
3526
3527      fn visit_seq<V>(
3528        self,
3529        mut v: V,
3530      ) -> Result<ChildUnaryPermissionArg, V::Error>
3531      where
3532        V: de::SeqAccess<'de>,
3533      {
3534        let mut granted_list = vec![];
3535        while let Some(value) = v.next_element::<String>()? {
3536          granted_list.push(value);
3537        }
3538        Ok(ChildUnaryPermissionArg::GrantedList(granted_list))
3539      }
3540    }
3541    deserializer.deserialize_any(ChildUnaryPermissionArgVisitor)
3542  }
3543}
3544
3545/// Directly deserializable from JS worker and test permission options.
3546#[derive(Debug, Eq, PartialEq)]
3547pub struct ChildPermissionsArg {
3548  env: ChildUnaryPermissionArg,
3549  net: ChildUnaryPermissionArg,
3550  ffi: ChildUnaryPermissionArg,
3551  import: ChildUnaryPermissionArg,
3552  read: ChildUnaryPermissionArg,
3553  run: ChildUnaryPermissionArg,
3554  sys: ChildUnaryPermissionArg,
3555  write: ChildUnaryPermissionArg,
3556}
3557
3558impl ChildPermissionsArg {
3559  pub fn inherit() -> Self {
3560    ChildPermissionsArg {
3561      env: ChildUnaryPermissionArg::Inherit,
3562      net: ChildUnaryPermissionArg::Inherit,
3563      ffi: ChildUnaryPermissionArg::Inherit,
3564      import: ChildUnaryPermissionArg::Inherit,
3565      read: ChildUnaryPermissionArg::Inherit,
3566      run: ChildUnaryPermissionArg::Inherit,
3567      sys: ChildUnaryPermissionArg::Inherit,
3568      write: ChildUnaryPermissionArg::Inherit,
3569    }
3570  }
3571
3572  pub fn none() -> Self {
3573    ChildPermissionsArg {
3574      env: ChildUnaryPermissionArg::NotGranted,
3575      net: ChildUnaryPermissionArg::NotGranted,
3576      ffi: ChildUnaryPermissionArg::NotGranted,
3577      import: ChildUnaryPermissionArg::NotGranted,
3578      read: ChildUnaryPermissionArg::NotGranted,
3579      run: ChildUnaryPermissionArg::NotGranted,
3580      sys: ChildUnaryPermissionArg::NotGranted,
3581      write: ChildUnaryPermissionArg::NotGranted,
3582    }
3583  }
3584}
3585
3586impl<'de> Deserialize<'de> for ChildPermissionsArg {
3587  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3588  where
3589    D: Deserializer<'de>,
3590  {
3591    struct ChildPermissionsArgVisitor;
3592    impl<'de> de::Visitor<'de> for ChildPermissionsArgVisitor {
3593      type Value = ChildPermissionsArg;
3594
3595      fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
3596        formatter.write_str("\"inherit\" or \"none\" or object")
3597      }
3598
3599      fn visit_unit<E>(self) -> Result<ChildPermissionsArg, E>
3600      where
3601        E: de::Error,
3602      {
3603        Ok(ChildPermissionsArg::inherit())
3604      }
3605
3606      fn visit_str<E>(self, v: &str) -> Result<ChildPermissionsArg, E>
3607      where
3608        E: de::Error,
3609      {
3610        if v == "inherit" {
3611          Ok(ChildPermissionsArg::inherit())
3612        } else if v == "none" {
3613          Ok(ChildPermissionsArg::none())
3614        } else {
3615          Err(de::Error::invalid_value(de::Unexpected::Str(v), &self))
3616        }
3617      }
3618
3619      fn visit_map<V>(self, mut v: V) -> Result<ChildPermissionsArg, V::Error>
3620      where
3621        V: de::MapAccess<'de>,
3622      {
3623        let mut child_permissions_arg = ChildPermissionsArg::none();
3624        while let Some((key, value)) =
3625          v.next_entry::<String, serde_json::Value>()?
3626        {
3627          if key == "env" {
3628            let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value);
3629            child_permissions_arg.env = arg.map_err(|e| {
3630              de::Error::custom(format!("(deno.permissions.env) {e}"))
3631            })?;
3632          } else if key == "net" {
3633            let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value);
3634            child_permissions_arg.net = arg.map_err(|e| {
3635              de::Error::custom(format!("(deno.permissions.net) {e}"))
3636            })?;
3637          } else if key == "ffi" {
3638            let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value);
3639            child_permissions_arg.ffi = arg.map_err(|e| {
3640              de::Error::custom(format!("(deno.permissions.ffi) {e}"))
3641            })?;
3642          } else if key == "import" {
3643            let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value);
3644            child_permissions_arg.import = arg.map_err(|e| {
3645              de::Error::custom(format!("(deno.permissions.import) {e}"))
3646            })?;
3647          } else if key == "read" {
3648            let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value);
3649            child_permissions_arg.read = arg.map_err(|e| {
3650              de::Error::custom(format!("(deno.permissions.read) {e}"))
3651            })?;
3652          } else if key == "run" {
3653            let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value);
3654            child_permissions_arg.run = arg.map_err(|e| {
3655              de::Error::custom(format!("(deno.permissions.run) {e}"))
3656            })?;
3657          } else if key == "sys" {
3658            let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value);
3659            child_permissions_arg.sys = arg.map_err(|e| {
3660              de::Error::custom(format!("(deno.permissions.sys) {e}"))
3661            })?;
3662          } else if key == "write" {
3663            let arg = serde_json::from_value::<ChildUnaryPermissionArg>(value);
3664            child_permissions_arg.write = arg.map_err(|e| {
3665              de::Error::custom(format!("(deno.permissions.write) {e}"))
3666            })?;
3667          } else {
3668            return Err(de::Error::custom("unknown permission name"));
3669          }
3670        }
3671        Ok(child_permissions_arg)
3672      }
3673    }
3674    deserializer.deserialize_any(ChildPermissionsArgVisitor)
3675  }
3676}
3677
3678/// Parses and normalizes permissions.
3679///
3680/// This trait is necessary because this crate doesn't have access
3681/// to the file system.
3682pub trait PermissionDescriptorParser: Debug + Send + Sync {
3683  fn parse_read_descriptor(
3684    &self,
3685    text: &str,
3686  ) -> Result<ReadDescriptor, PathResolveError>;
3687
3688  fn parse_write_descriptor(
3689    &self,
3690    text: &str,
3691  ) -> Result<WriteDescriptor, PathResolveError>;
3692
3693  fn parse_net_descriptor(
3694    &self,
3695    text: &str,
3696  ) -> Result<NetDescriptor, NetDescriptorParseError>;
3697
3698  fn parse_net_descriptor_from_url(
3699    &self,
3700    url: &Url,
3701  ) -> Result<NetDescriptor, NetDescriptorFromUrlParseError> {
3702    NetDescriptor::from_url(url)
3703  }
3704
3705  fn parse_import_descriptor(
3706    &self,
3707    text: &str,
3708  ) -> Result<ImportDescriptor, NetDescriptorParseError>;
3709
3710  fn parse_import_descriptor_from_url(
3711    &self,
3712    url: &Url,
3713  ) -> Result<ImportDescriptor, NetDescriptorFromUrlParseError> {
3714    ImportDescriptor::from_url(url)
3715  }
3716
3717  fn parse_env_descriptor(
3718    &self,
3719    text: &str,
3720  ) -> Result<EnvDescriptor, EnvDescriptorParseError>;
3721
3722  fn parse_sys_descriptor(
3723    &self,
3724    text: &str,
3725  ) -> Result<SysDescriptor, SysDescriptorParseError>;
3726
3727  fn parse_allow_run_descriptor(
3728    &self,
3729    text: &str,
3730  ) -> Result<AllowRunDescriptorParseResult, RunDescriptorParseError>;
3731
3732  fn parse_deny_run_descriptor(
3733    &self,
3734    text: &str,
3735  ) -> Result<DenyRunDescriptor, PathResolveError>;
3736
3737  fn parse_ffi_descriptor(
3738    &self,
3739    text: &str,
3740  ) -> Result<FfiDescriptor, PathResolveError>;
3741
3742  // queries
3743
3744  fn parse_path_query(
3745    &self,
3746    path: &str,
3747  ) -> Result<PathQueryDescriptor, PathResolveError>;
3748
3749  fn parse_run_query(
3750    &self,
3751    requested: &str,
3752  ) -> Result<RunQueryDescriptor, RunDescriptorParseError>;
3753}
3754
3755static IS_STANDALONE: AtomicFlag = AtomicFlag::lowered();
3756
3757pub fn mark_standalone() {
3758  IS_STANDALONE.raise();
3759}
3760
3761pub fn is_standalone() -> bool {
3762  IS_STANDALONE.is_raised()
3763}
3764
3765#[cfg(test)]
3766mod tests {
3767  use std::net::Ipv4Addr;
3768
3769  use deno_core::serde_json::json;
3770  use fqdn::fqdn;
3771  use prompter::tests::*;
3772
3773  use super::*;
3774
3775  // Creates vector of strings, Vec<String>
3776  macro_rules! svec {
3777      ($($x:expr),*) => (vec![$($x.to_string()),*]);
3778  }
3779
3780  #[derive(Debug, Clone)]
3781  struct TestPermissionDescriptorParser;
3782
3783  impl TestPermissionDescriptorParser {
3784    fn join_path_with_root(&self, path: &str) -> PathBuf {
3785      if path.starts_with("C:\\") {
3786        PathBuf::from(path)
3787      } else {
3788        PathBuf::from("/").join(path)
3789      }
3790    }
3791  }
3792
3793  impl PermissionDescriptorParser for TestPermissionDescriptorParser {
3794    fn parse_read_descriptor(
3795      &self,
3796      text: &str,
3797    ) -> Result<ReadDescriptor, PathResolveError> {
3798      Ok(ReadDescriptor(self.join_path_with_root(text)))
3799    }
3800
3801    fn parse_write_descriptor(
3802      &self,
3803      text: &str,
3804    ) -> Result<WriteDescriptor, PathResolveError> {
3805      Ok(WriteDescriptor(self.join_path_with_root(text)))
3806    }
3807
3808    fn parse_net_descriptor(
3809      &self,
3810      text: &str,
3811    ) -> Result<NetDescriptor, NetDescriptorParseError> {
3812      NetDescriptor::parse(text)
3813    }
3814
3815    fn parse_import_descriptor(
3816      &self,
3817      text: &str,
3818    ) -> Result<ImportDescriptor, NetDescriptorParseError> {
3819      ImportDescriptor::parse(text)
3820    }
3821
3822    fn parse_env_descriptor(
3823      &self,
3824      text: &str,
3825    ) -> Result<EnvDescriptor, EnvDescriptorParseError> {
3826      Ok(EnvDescriptor::new(text))
3827    }
3828
3829    fn parse_sys_descriptor(
3830      &self,
3831      text: &str,
3832    ) -> Result<SysDescriptor, SysDescriptorParseError> {
3833      SysDescriptor::parse(text.to_string())
3834    }
3835
3836    fn parse_allow_run_descriptor(
3837      &self,
3838      text: &str,
3839    ) -> Result<AllowRunDescriptorParseResult, RunDescriptorParseError> {
3840      Ok(AllowRunDescriptorParseResult::Descriptor(
3841        AllowRunDescriptor(self.join_path_with_root(text)),
3842      ))
3843    }
3844
3845    fn parse_deny_run_descriptor(
3846      &self,
3847      text: &str,
3848    ) -> Result<DenyRunDescriptor, PathResolveError> {
3849      if text.contains("/") {
3850        Ok(DenyRunDescriptor::Path(self.join_path_with_root(text)))
3851      } else {
3852        Ok(DenyRunDescriptor::Name(text.to_string()))
3853      }
3854    }
3855
3856    fn parse_ffi_descriptor(
3857      &self,
3858      text: &str,
3859    ) -> Result<FfiDescriptor, PathResolveError> {
3860      Ok(FfiDescriptor(self.join_path_with_root(text)))
3861    }
3862
3863    fn parse_path_query(
3864      &self,
3865      path: &str,
3866    ) -> Result<PathQueryDescriptor, PathResolveError> {
3867      Ok(PathQueryDescriptor {
3868        resolved: self.join_path_with_root(path),
3869        requested: path.to_string(),
3870      })
3871    }
3872
3873    fn parse_run_query(
3874      &self,
3875      requested: &str,
3876    ) -> Result<RunQueryDescriptor, RunDescriptorParseError> {
3877      RunQueryDescriptor::parse(requested).map_err(Into::into)
3878    }
3879  }
3880
3881  #[test]
3882  fn check_paths() {
3883    set_prompter(Box::new(TestPrompter));
3884    let allowlist = svec!["/a/specific/dir/name", "/a/specific", "/b/c"];
3885
3886    let parser = TestPermissionDescriptorParser;
3887    let perms = Permissions::from_options(
3888      &parser,
3889      &PermissionsOptions {
3890        allow_read: Some(allowlist.clone()),
3891        allow_write: Some(allowlist.clone()),
3892        allow_ffi: Some(allowlist),
3893        ..Default::default()
3894      },
3895    )
3896    .unwrap();
3897    let mut perms = PermissionsContainer::new(Arc::new(parser), perms);
3898
3899    let cases = [
3900      // Inside of /a/specific and /a/specific/dir/name
3901      ("/a/specific/dir/name", true),
3902      // Inside of /a/specific but outside of /a/specific/dir/name
3903      ("/a/specific/dir", true),
3904      // Inside of /a/specific and /a/specific/dir/name
3905      ("/a/specific/dir/name/inner", true),
3906      // Inside of /a/specific but outside of /a/specific/dir/name
3907      ("/a/specific/other/dir", true),
3908      // Exact match with /b/c
3909      ("/b/c", true),
3910      // Sub path within /b/c
3911      ("/b/c/sub/path", true),
3912      // Sub path within /b/c, needs normalizing
3913      ("/b/c/sub/path/../path/.", true),
3914      // Inside of /b but outside of /b/c
3915      ("/b/e", false),
3916      // Inside of /a but outside of /a/specific
3917      ("/a/b", false),
3918    ];
3919
3920    for (path, is_ok) in cases {
3921      assert_eq!(perms.check_read(path, "api").is_ok(), is_ok);
3922      assert_eq!(perms.check_write(path, "api").is_ok(), is_ok);
3923      assert_eq!(perms.check_ffi(path).is_ok(), is_ok);
3924    }
3925  }
3926
3927  #[test]
3928  fn test_check_net_with_values() {
3929    set_prompter(Box::new(TestPrompter));
3930    let parser = TestPermissionDescriptorParser;
3931    let mut perms = Permissions::from_options(
3932      &parser,
3933      &PermissionsOptions {
3934        allow_net: Some(svec![
3935          "localhost",
3936          "deno.land",
3937          "github.com:3000",
3938          "127.0.0.1",
3939          "172.16.0.2:8000",
3940          "www.github.com:443",
3941          "80.example.com:80",
3942          "443.example.com:443"
3943        ]),
3944        ..Default::default()
3945      },
3946    )
3947    .unwrap();
3948
3949    let domain_tests = vec![
3950      ("localhost", 1234, true),
3951      ("deno.land", 0, true),
3952      ("deno.land", 3000, true),
3953      ("deno.lands", 0, false),
3954      ("deno.lands", 3000, false),
3955      ("github.com", 3000, true),
3956      ("github.com", 0, false),
3957      ("github.com", 2000, false),
3958      ("github.net", 3000, false),
3959      ("127.0.0.1", 0, true),
3960      ("127.0.0.1", 3000, true),
3961      ("127.0.0.2", 0, false),
3962      ("127.0.0.2", 3000, false),
3963      ("172.16.0.2", 8000, true),
3964      ("172.16.0.2", 0, false),
3965      ("172.16.0.2", 6000, false),
3966      ("172.16.0.1", 8000, false),
3967      ("443.example.com", 444, false),
3968      ("80.example.com", 81, false),
3969      ("80.example.com", 80, true),
3970      // Just some random hosts that should err
3971      ("somedomain", 0, false),
3972      ("192.168.0.1", 0, false),
3973    ];
3974
3975    for (host, port, is_ok) in domain_tests {
3976      let host = Host::parse(host).unwrap();
3977      let descriptor = NetDescriptor(host, Some(port));
3978      assert_eq!(
3979        is_ok,
3980        perms.net.check(&descriptor, None).is_ok(),
3981        "{descriptor}",
3982      );
3983    }
3984  }
3985
3986  #[test]
3987  fn test_check_net_only_flag() {
3988    set_prompter(Box::new(TestPrompter));
3989    let parser = TestPermissionDescriptorParser;
3990    let mut perms = Permissions::from_options(
3991      &parser,
3992      &PermissionsOptions {
3993        allow_net: Some(svec![]), // this means `--allow-net` is present without values following `=` sign
3994        ..Default::default()
3995      },
3996    )
3997    .unwrap();
3998
3999    let domain_tests = vec![
4000      ("localhost", 1234),
4001      ("deno.land", 0),
4002      ("deno.land", 3000),
4003      ("deno.lands", 0),
4004      ("deno.lands", 3000),
4005      ("github.com", 3000),
4006      ("github.com", 0),
4007      ("github.com", 2000),
4008      ("github.net", 3000),
4009      ("127.0.0.1", 0),
4010      ("127.0.0.1", 3000),
4011      ("127.0.0.2", 0),
4012      ("127.0.0.2", 3000),
4013      ("172.16.0.2", 8000),
4014      ("172.16.0.2", 0),
4015      ("172.16.0.2", 6000),
4016      ("172.16.0.1", 8000),
4017      ("somedomain", 0),
4018      ("192.168.0.1", 0),
4019    ];
4020
4021    for (host_str, port) in domain_tests {
4022      let host = Host::parse(host_str).unwrap();
4023      let descriptor = NetDescriptor(host, Some(port));
4024      assert!(
4025        perms.net.check(&descriptor, None).is_ok(),
4026        "expected {host_str}:{port} to pass"
4027      );
4028    }
4029  }
4030
4031  #[test]
4032  fn test_check_net_no_flag() {
4033    set_prompter(Box::new(TestPrompter));
4034    let parser = TestPermissionDescriptorParser;
4035    let mut perms = Permissions::from_options(
4036      &parser,
4037      &PermissionsOptions {
4038        allow_net: None,
4039        ..Default::default()
4040      },
4041    )
4042    .unwrap();
4043
4044    let domain_tests = vec![
4045      ("localhost", 1234),
4046      ("deno.land", 0),
4047      ("deno.land", 3000),
4048      ("deno.lands", 0),
4049      ("deno.lands", 3000),
4050      ("github.com", 3000),
4051      ("github.com", 0),
4052      ("github.com", 2000),
4053      ("github.net", 3000),
4054      ("127.0.0.1", 0),
4055      ("127.0.0.1", 3000),
4056      ("127.0.0.2", 0),
4057      ("127.0.0.2", 3000),
4058      ("172.16.0.2", 8000),
4059      ("172.16.0.2", 0),
4060      ("172.16.0.2", 6000),
4061      ("172.16.0.1", 8000),
4062      ("somedomain", 0),
4063      ("192.168.0.1", 0),
4064    ];
4065
4066    for (host_str, port) in domain_tests {
4067      let host = Host::parse(host_str).unwrap();
4068      let descriptor = NetDescriptor(host, Some(port));
4069      assert!(
4070        perms.net.check(&descriptor, None).is_err(),
4071        "expected {host_str}:{port} to fail"
4072      );
4073    }
4074  }
4075
4076  #[test]
4077  fn test_check_net_url() {
4078    let parser = TestPermissionDescriptorParser;
4079    let perms = Permissions::from_options(
4080      &parser,
4081      &PermissionsOptions {
4082        allow_net: Some(svec![
4083          "localhost",
4084          "deno.land",
4085          "github.com:3000",
4086          "127.0.0.1",
4087          "172.16.0.2:8000",
4088          "www.github.com:443"
4089        ]),
4090        ..Default::default()
4091      },
4092    )
4093    .unwrap();
4094    let mut perms = PermissionsContainer::new(Arc::new(parser), perms);
4095
4096    let url_tests = vec![
4097      // Any protocol + port for localhost should be ok, since we don't specify
4098      ("http://localhost", true),
4099      ("https://localhost", true),
4100      ("https://localhost:4443", true),
4101      ("tcp://localhost:5000", true),
4102      ("udp://localhost:6000", true),
4103      // Correct domain + any port and protocol should be ok incorrect shouldn't
4104      ("https://deno.land/std/example/welcome.ts", true),
4105      ("https://deno.land:3000/std/example/welcome.ts", true),
4106      ("https://deno.lands/std/example/welcome.ts", false),
4107      ("https://deno.lands:3000/std/example/welcome.ts", false),
4108      // Correct domain + port should be ok all other combinations should err
4109      ("https://github.com:3000/denoland/deno", true),
4110      ("https://github.com/denoland/deno", false),
4111      ("https://github.com:2000/denoland/deno", false),
4112      ("https://github.net:3000/denoland/deno", false),
4113      // Correct ipv4 address + any port should be ok others should err
4114      ("tcp://127.0.0.1", true),
4115      ("https://127.0.0.1", true),
4116      ("tcp://127.0.0.1:3000", true),
4117      ("https://127.0.0.1:3000", true),
4118      ("tcp://127.0.0.2", false),
4119      ("https://127.0.0.2", false),
4120      ("tcp://127.0.0.2:3000", false),
4121      ("https://127.0.0.2:3000", false),
4122      // Correct address + port should be ok all other combinations should err
4123      ("tcp://172.16.0.2:8000", true),
4124      ("https://172.16.0.2:8000", true),
4125      ("tcp://172.16.0.2", false),
4126      ("https://172.16.0.2", false),
4127      ("tcp://172.16.0.2:6000", false),
4128      ("https://172.16.0.2:6000", false),
4129      ("tcp://172.16.0.1:8000", false),
4130      ("https://172.16.0.1:8000", false),
4131      // Testing issue #6531 (Network permissions check doesn't account for well-known default ports) so we dont regress
4132      ("https://www.github.com:443/robots.txt", true),
4133    ];
4134
4135    for (url_str, is_ok) in url_tests {
4136      let u = Url::parse(url_str).unwrap();
4137      assert_eq!(is_ok, perms.check_net_url(&u, "api()").is_ok(), "{}", u);
4138    }
4139  }
4140
4141  #[test]
4142  fn check_specifiers() {
4143    set_prompter(Box::new(TestPrompter));
4144    let read_allowlist = if cfg!(target_os = "windows") {
4145      svec!["C:\\a"]
4146    } else {
4147      svec!["/a"]
4148    };
4149    let parser = TestPermissionDescriptorParser;
4150    let perms = Permissions::from_options(
4151      &parser,
4152      &PermissionsOptions {
4153        allow_read: Some(read_allowlist),
4154        allow_import: Some(svec!["localhost"]),
4155        ..Default::default()
4156      },
4157    )
4158    .unwrap();
4159    let perms = PermissionsContainer::new(Arc::new(parser), perms);
4160
4161    let mut fixtures = vec![
4162      (
4163        ModuleSpecifier::parse("http://localhost:4545/mod.ts").unwrap(),
4164        CheckSpecifierKind::Static,
4165        true,
4166      ),
4167      (
4168        ModuleSpecifier::parse("http://localhost:4545/mod.ts").unwrap(),
4169        CheckSpecifierKind::Dynamic,
4170        true,
4171      ),
4172      (
4173        ModuleSpecifier::parse("http://deno.land/x/mod.ts").unwrap(),
4174        CheckSpecifierKind::Dynamic,
4175        false,
4176      ),
4177      (
4178        ModuleSpecifier::parse("data:text/plain,Hello%2C%20Deno!").unwrap(),
4179        CheckSpecifierKind::Dynamic,
4180        true,
4181      ),
4182    ];
4183
4184    if cfg!(target_os = "windows") {
4185      fixtures.push((
4186        ModuleSpecifier::parse("file:///C:/a/mod.ts").unwrap(),
4187        CheckSpecifierKind::Dynamic,
4188        true,
4189      ));
4190      fixtures.push((
4191        ModuleSpecifier::parse("file:///C:/b/mod.ts").unwrap(),
4192        CheckSpecifierKind::Static,
4193        true,
4194      ));
4195      fixtures.push((
4196        ModuleSpecifier::parse("file:///C:/b/mod.ts").unwrap(),
4197        CheckSpecifierKind::Dynamic,
4198        false,
4199      ));
4200    } else {
4201      fixtures.push((
4202        ModuleSpecifier::parse("file:///a/mod.ts").unwrap(),
4203        CheckSpecifierKind::Dynamic,
4204        true,
4205      ));
4206      fixtures.push((
4207        ModuleSpecifier::parse("file:///b/mod.ts").unwrap(),
4208        CheckSpecifierKind::Static,
4209        true,
4210      ));
4211      fixtures.push((
4212        ModuleSpecifier::parse("file:///b/mod.ts").unwrap(),
4213        CheckSpecifierKind::Dynamic,
4214        false,
4215      ));
4216    }
4217
4218    for (specifier, kind, expected) in fixtures {
4219      assert_eq!(
4220        perms.check_specifier(&specifier, kind).is_ok(),
4221        expected,
4222        "{}",
4223        specifier,
4224      );
4225    }
4226  }
4227
4228  #[test]
4229  fn test_query() {
4230    set_prompter(Box::new(TestPrompter));
4231    let parser = TestPermissionDescriptorParser;
4232    let perms1 = Permissions::allow_all();
4233    let perms2 = Permissions::from_options(
4234      &parser,
4235      &PermissionsOptions {
4236        allow_read: Some(svec!["/foo"]),
4237        allow_write: Some(svec!["/foo"]),
4238        allow_ffi: Some(svec!["/foo"]),
4239        allow_net: Some(svec!["127.0.0.1:8000"]),
4240        allow_env: Some(svec!["HOME"]),
4241        allow_sys: Some(svec!["hostname"]),
4242        allow_run: Some(svec!["/deno"]),
4243        allow_all: false,
4244        ..Default::default()
4245      },
4246    )
4247    .unwrap();
4248    let perms3 = Permissions::from_options(
4249      &parser,
4250      &PermissionsOptions {
4251        deny_read: Some(svec!["/foo"]),
4252        deny_write: Some(svec!["/foo"]),
4253        deny_ffi: Some(svec!["/foo"]),
4254        deny_net: Some(svec!["127.0.0.1:8000"]),
4255        deny_env: Some(svec!["HOME"]),
4256        deny_sys: Some(svec!["hostname"]),
4257        deny_run: Some(svec!["deno"]),
4258        ..Default::default()
4259      },
4260    )
4261    .unwrap();
4262    let perms4 = Permissions::from_options(
4263      &parser,
4264      &PermissionsOptions {
4265        allow_read: Some(vec![]),
4266        deny_read: Some(svec!["/foo"]),
4267        allow_write: Some(vec![]),
4268        deny_write: Some(svec!["/foo"]),
4269        allow_ffi: Some(vec![]),
4270        deny_ffi: Some(svec!["/foo"]),
4271        allow_net: Some(vec![]),
4272        deny_net: Some(svec!["127.0.0.1:8000"]),
4273        allow_env: Some(vec![]),
4274        deny_env: Some(svec!["HOME"]),
4275        allow_sys: Some(vec![]),
4276        deny_sys: Some(svec!["hostname"]),
4277        allow_run: Some(vec![]),
4278        deny_run: Some(svec!["deno"]),
4279        ..Default::default()
4280      },
4281    )
4282    .unwrap();
4283    #[rustfmt::skip]
4284    {
4285      let read_query = |path: &str| parser.parse_path_query(path).unwrap().into_read();
4286      let write_query = |path: &str| parser.parse_path_query(path).unwrap().into_write();
4287      let ffi_query = |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
4288      assert_eq!(perms1.read.query(None), PermissionState::Granted);
4289      assert_eq!(perms1.read.query(Some(&read_query("/foo"))), PermissionState::Granted);
4290      assert_eq!(perms2.read.query(None), PermissionState::Prompt);
4291      assert_eq!(perms2.read.query(Some(&read_query("/foo"))), PermissionState::Granted);
4292      assert_eq!(perms2.read.query(Some(&read_query("/foo/bar"))), PermissionState::Granted);
4293      assert_eq!(perms3.read.query(None), PermissionState::Prompt);
4294      assert_eq!(perms3.read.query(Some(&read_query("/foo"))), PermissionState::Denied);
4295      assert_eq!(perms3.read.query(Some(&read_query("/foo/bar"))), PermissionState::Denied);
4296      assert_eq!(perms4.read.query(None), PermissionState::GrantedPartial);
4297      assert_eq!(perms4.read.query(Some(&read_query("/foo"))), PermissionState::Denied);
4298      assert_eq!(perms4.read.query(Some(&read_query("/foo/bar"))), PermissionState::Denied);
4299      assert_eq!(perms4.read.query(Some(&read_query("/bar"))), PermissionState::Granted);
4300      assert_eq!(perms1.write.query(None), PermissionState::Granted);
4301      assert_eq!(perms1.write.query(Some(&write_query("/foo"))), PermissionState::Granted);
4302      assert_eq!(perms2.write.query(None), PermissionState::Prompt);
4303      assert_eq!(perms2.write.query(Some(&write_query("/foo"))), PermissionState::Granted);
4304      assert_eq!(perms2.write.query(Some(&write_query("/foo/bar"))), PermissionState::Granted);
4305      assert_eq!(perms3.write.query(None), PermissionState::Prompt);
4306      assert_eq!(perms3.write.query(Some(&write_query("/foo"))), PermissionState::Denied);
4307      assert_eq!(perms3.write.query(Some(&write_query("/foo/bar"))), PermissionState::Denied);
4308      assert_eq!(perms4.write.query(None), PermissionState::GrantedPartial);
4309      assert_eq!(perms4.write.query(Some(&write_query("/foo"))), PermissionState::Denied);
4310      assert_eq!(perms4.write.query(Some(&write_query("/foo/bar"))), PermissionState::Denied);
4311      assert_eq!(perms4.write.query(Some(&write_query("/bar"))), PermissionState::Granted);
4312      assert_eq!(perms1.ffi.query(None), PermissionState::Granted);
4313      assert_eq!(perms1.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Granted);
4314      assert_eq!(perms2.ffi.query(None), PermissionState::Prompt);
4315      assert_eq!(perms2.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Granted);
4316      assert_eq!(perms2.ffi.query(Some(&ffi_query("/foo/bar"))), PermissionState::Granted);
4317      assert_eq!(perms3.ffi.query(None), PermissionState::Prompt);
4318      assert_eq!(perms3.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Denied);
4319      assert_eq!(perms3.ffi.query(Some(&ffi_query("/foo/bar"))), PermissionState::Denied);
4320      assert_eq!(perms4.ffi.query(None), PermissionState::GrantedPartial);
4321      assert_eq!(perms4.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Denied);
4322      assert_eq!(perms4.ffi.query(Some(&ffi_query("/foo/bar"))), PermissionState::Denied);
4323      assert_eq!(perms4.ffi.query(Some(&ffi_query("/bar"))), PermissionState::Granted);
4324      assert_eq!(perms1.net.query(None), PermissionState::Granted);
4325      assert_eq!(perms1.net.query(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), None))), PermissionState::Granted);
4326      assert_eq!(perms2.net.query(None), PermissionState::Prompt);
4327      assert_eq!(perms2.net.query(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)))), PermissionState::Granted);
4328      assert_eq!(perms3.net.query(None), PermissionState::Prompt);
4329      assert_eq!(perms3.net.query(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)))), PermissionState::Denied);
4330      assert_eq!(perms4.net.query(None), PermissionState::GrantedPartial);
4331      assert_eq!(perms4.net.query(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)))), PermissionState::Denied);
4332      assert_eq!(perms4.net.query(Some(&NetDescriptor(Host::must_parse("192.168.0.1"), Some(8000)))), PermissionState::Granted);
4333      assert_eq!(perms1.env.query(None), PermissionState::Granted);
4334      assert_eq!(perms1.env.query(Some("HOME")), PermissionState::Granted);
4335      assert_eq!(perms2.env.query(None), PermissionState::Prompt);
4336      assert_eq!(perms2.env.query(Some("HOME")), PermissionState::Granted);
4337      assert_eq!(perms3.env.query(None), PermissionState::Prompt);
4338      assert_eq!(perms3.env.query(Some("HOME")), PermissionState::Denied);
4339      assert_eq!(perms4.env.query(None), PermissionState::GrantedPartial);
4340      assert_eq!(perms4.env.query(Some("HOME")), PermissionState::Denied);
4341      assert_eq!(perms4.env.query(Some("AWAY")), PermissionState::Granted);
4342      let sys_desc = |name: &str| SysDescriptor::parse(name.to_string()).unwrap();
4343      assert_eq!(perms1.sys.query(None), PermissionState::Granted);
4344      assert_eq!(perms1.sys.query(Some(&sys_desc("osRelease"))), PermissionState::Granted);
4345      assert_eq!(perms2.sys.query(None), PermissionState::Prompt);
4346      assert_eq!(perms2.sys.query(Some(&sys_desc("hostname"))), PermissionState::Granted);
4347      assert_eq!(perms3.sys.query(None), PermissionState::Prompt);
4348      assert_eq!(perms3.sys.query(Some(&sys_desc("hostname"))), PermissionState::Denied);
4349      assert_eq!(perms4.sys.query(None), PermissionState::GrantedPartial);
4350      assert_eq!(perms4.sys.query(Some(&sys_desc("hostname"))), PermissionState::Denied);
4351      assert_eq!(perms4.sys.query(Some(&sys_desc("uid"))), PermissionState::Granted);
4352      assert_eq!(perms1.run.query(None), PermissionState::Granted);
4353      let deno_run_query = RunQueryDescriptor::Path {
4354        requested: "deno".to_string(),
4355        resolved: PathBuf::from("/deno"),
4356      };
4357      let node_run_query = RunQueryDescriptor::Path {
4358        requested: "node".to_string(),
4359        resolved: PathBuf::from("/node"),
4360      };
4361      assert_eq!(perms1.run.query(Some(&deno_run_query)), PermissionState::Granted);
4362      assert_eq!(perms1.write.query(Some(&write_query("/deno"))), PermissionState::Granted);
4363      assert_eq!(perms2.run.query(None), PermissionState::Prompt);
4364      assert_eq!(perms2.run.query(Some(&deno_run_query)), PermissionState::Granted);
4365      assert_eq!(perms2.write.query(Some(&write_query("/deno"))), PermissionState::Denied);
4366      assert_eq!(perms3.run.query(None), PermissionState::Prompt);
4367      assert_eq!(perms3.run.query(Some(&deno_run_query)), PermissionState::Denied);
4368      assert_eq!(perms4.run.query(None), PermissionState::GrantedPartial);
4369      assert_eq!(perms4.run.query(Some(&deno_run_query)), PermissionState::Denied);
4370      assert_eq!(perms4.run.query(Some(&node_run_query)), PermissionState::Granted);
4371    };
4372  }
4373
4374  #[test]
4375  fn test_request() {
4376    set_prompter(Box::new(TestPrompter));
4377    let parser = TestPermissionDescriptorParser;
4378    let mut perms: Permissions = Permissions::none_with_prompt();
4379    let mut perms_no_prompt: Permissions = Permissions::none_without_prompt();
4380    let read_query =
4381      |path: &str| parser.parse_path_query(path).unwrap().into_read();
4382    let write_query =
4383      |path: &str| parser.parse_path_query(path).unwrap().into_write();
4384    let ffi_query =
4385      |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
4386    #[rustfmt::skip]
4387    {
4388      let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
4389      prompt_value.set(true);
4390      assert_eq!(perms.read.request(Some(&read_query("/foo"))), PermissionState::Granted);
4391      assert_eq!(perms.read.query(None), PermissionState::Prompt);
4392      prompt_value.set(false);
4393      assert_eq!(perms.read.request(Some(&read_query("/foo/bar"))), PermissionState::Granted);
4394      prompt_value.set(false);
4395      assert_eq!(perms.write.request(Some(&write_query("/foo"))), PermissionState::Denied);
4396      assert_eq!(perms.write.query(Some(&write_query("/foo/bar"))), PermissionState::Prompt);
4397      prompt_value.set(true);
4398      assert_eq!(perms.write.request(None), PermissionState::Denied);
4399      prompt_value.set(false);
4400      assert_eq!(perms.ffi.request(Some(&ffi_query("/foo"))), PermissionState::Denied);
4401      assert_eq!(perms.ffi.query(Some(&ffi_query("/foo/bar"))), PermissionState::Prompt);
4402      prompt_value.set(true);
4403      assert_eq!(perms.ffi.request(None), PermissionState::Denied);
4404      prompt_value.set(true);
4405      assert_eq!(perms.net.request(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), None))), PermissionState::Granted);
4406      prompt_value.set(false);
4407      assert_eq!(perms.net.request(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)))), PermissionState::Granted);
4408      prompt_value.set(true);
4409      assert_eq!(perms.env.request(Some("HOME")), PermissionState::Granted);
4410      assert_eq!(perms.env.query(None), PermissionState::Prompt);
4411      prompt_value.set(false);
4412      assert_eq!(perms.env.request(Some("HOME")), PermissionState::Granted);
4413      prompt_value.set(true);
4414      let sys_desc = |name: &str| SysDescriptor::parse(name.to_string()).unwrap();
4415      assert_eq!(perms.sys.request(Some(&sys_desc("hostname"))), PermissionState::Granted);
4416      assert_eq!(perms.sys.query(None), PermissionState::Prompt);
4417      prompt_value.set(false);
4418      assert_eq!(perms.sys.request(Some(&sys_desc("hostname"))), PermissionState::Granted);
4419      prompt_value.set(true);
4420      let run_query = RunQueryDescriptor::Path {
4421        requested: "deno".to_string(),
4422        resolved: PathBuf::from("/deno"),
4423      };
4424      assert_eq!(perms.run.request(Some(&run_query)), PermissionState::Granted);
4425      assert_eq!(perms.run.query(None), PermissionState::Prompt);
4426      prompt_value.set(false);
4427      assert_eq!(perms.run.request(Some(&run_query)), PermissionState::Granted);
4428      assert_eq!(perms_no_prompt.read.request(Some(&read_query("/foo"))), PermissionState::Denied);
4429    };
4430  }
4431
4432  #[test]
4433  fn test_revoke() {
4434    set_prompter(Box::new(TestPrompter));
4435    let parser = TestPermissionDescriptorParser;
4436    let mut perms = Permissions::from_options(
4437      &parser,
4438      &PermissionsOptions {
4439        allow_read: Some(svec!["/foo", "/foo/baz"]),
4440        allow_write: Some(svec!["/foo", "/foo/baz"]),
4441        allow_ffi: Some(svec!["/foo", "/foo/baz"]),
4442        allow_net: Some(svec!["127.0.0.1", "127.0.0.1:8000"]),
4443        allow_env: Some(svec!["HOME"]),
4444        allow_sys: Some(svec!["hostname"]),
4445        allow_run: Some(svec!["/deno"]),
4446        ..Default::default()
4447      },
4448    )
4449    .unwrap();
4450    let read_query =
4451      |path: &str| parser.parse_path_query(path).unwrap().into_read();
4452    let write_query =
4453      |path: &str| parser.parse_path_query(path).unwrap().into_write();
4454    let ffi_query =
4455      |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
4456    #[rustfmt::skip]
4457    {
4458      assert_eq!(perms.read.revoke(Some(&read_query("/foo/bar"))), PermissionState::Prompt);
4459      assert_eq!(perms.read.query(Some(&read_query("/foo"))), PermissionState::Prompt);
4460      assert_eq!(perms.read.query(Some(&read_query("/foo/baz"))), PermissionState::Granted);
4461      assert_eq!(perms.write.revoke(Some(&write_query("/foo/bar"))), PermissionState::Prompt);
4462      assert_eq!(perms.write.query(Some(&write_query("/foo"))), PermissionState::Prompt);
4463      assert_eq!(perms.write.query(Some(&write_query("/foo/baz"))), PermissionState::Granted);
4464      assert_eq!(perms.ffi.revoke(Some(&ffi_query("/foo/bar"))), PermissionState::Prompt);
4465      assert_eq!(perms.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Prompt);
4466      assert_eq!(perms.ffi.query(Some(&ffi_query("/foo/baz"))), PermissionState::Granted);
4467      assert_eq!(perms.net.revoke(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), Some(9000)))), PermissionState::Prompt);
4468      assert_eq!(perms.net.query(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), None))), PermissionState::Prompt);
4469      assert_eq!(perms.net.query(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)))), PermissionState::Granted);
4470      assert_eq!(perms.env.revoke(Some("HOME")), PermissionState::Prompt);
4471      assert_eq!(perms.env.revoke(Some("hostname")), PermissionState::Prompt);
4472      let run_query = RunQueryDescriptor::Path {
4473        requested: "deno".to_string(),
4474        resolved: PathBuf::from("/deno"),
4475      };
4476      assert_eq!(perms.run.revoke(Some(&run_query)), PermissionState::Prompt);
4477    };
4478  }
4479
4480  #[test]
4481  fn test_check() {
4482    set_prompter(Box::new(TestPrompter));
4483    let mut perms = Permissions::none_with_prompt();
4484    let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
4485    let parser = TestPermissionDescriptorParser;
4486    let read_query =
4487      |path: &str| parser.parse_path_query(path).unwrap().into_read();
4488    let write_query =
4489      |path: &str| parser.parse_path_query(path).unwrap().into_write();
4490    let ffi_query =
4491      |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
4492
4493    prompt_value.set(true);
4494    assert!(perms.read.check(&read_query("/foo"), None).is_ok());
4495    prompt_value.set(false);
4496    assert!(perms.read.check(&read_query("/foo"), None).is_ok());
4497    assert!(perms.read.check(&read_query("/bar"), None).is_err());
4498
4499    prompt_value.set(true);
4500    assert!(perms.write.check(&write_query("/foo"), None).is_ok());
4501    prompt_value.set(false);
4502    assert!(perms.write.check(&write_query("/foo"), None).is_ok());
4503    assert!(perms.write.check(&write_query("/bar"), None).is_err());
4504
4505    prompt_value.set(true);
4506    assert!(perms.ffi.check(&ffi_query("/foo"), None).is_ok());
4507    prompt_value.set(false);
4508    assert!(perms.ffi.check(&ffi_query("/foo"), None).is_ok());
4509    assert!(perms.ffi.check(&ffi_query("/bar"), None).is_err());
4510
4511    prompt_value.set(true);
4512    assert!(perms
4513      .net
4514      .check(
4515        &NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)),
4516        None
4517      )
4518      .is_ok());
4519    prompt_value.set(false);
4520    assert!(perms
4521      .net
4522      .check(
4523        &NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)),
4524        None
4525      )
4526      .is_ok());
4527    assert!(perms
4528      .net
4529      .check(
4530        &NetDescriptor(Host::must_parse("127.0.0.1"), Some(8001)),
4531        None
4532      )
4533      .is_err());
4534    assert!(perms
4535      .net
4536      .check(&NetDescriptor(Host::must_parse("127.0.0.1"), None), None)
4537      .is_err());
4538    assert!(perms
4539      .net
4540      .check(
4541        &NetDescriptor(Host::must_parse("deno.land"), Some(8000)),
4542        None
4543      )
4544      .is_err());
4545    assert!(perms
4546      .net
4547      .check(&NetDescriptor(Host::must_parse("deno.land"), None), None)
4548      .is_err());
4549
4550    #[allow(clippy::disallowed_methods)]
4551    let cwd = std::env::current_dir().unwrap();
4552    prompt_value.set(true);
4553    assert!(perms
4554      .run
4555      .check(
4556        &RunQueryDescriptor::Path {
4557          requested: "cat".to_string(),
4558          resolved: cwd.join("cat")
4559        },
4560        None
4561      )
4562      .is_ok());
4563    prompt_value.set(false);
4564    assert!(perms
4565      .run
4566      .check(
4567        &RunQueryDescriptor::Path {
4568          requested: "cat".to_string(),
4569          resolved: cwd.join("cat")
4570        },
4571        None
4572      )
4573      .is_ok());
4574    assert!(perms
4575      .run
4576      .check(
4577        &RunQueryDescriptor::Path {
4578          requested: "ls".to_string(),
4579          resolved: cwd.join("ls")
4580        },
4581        None
4582      )
4583      .is_err());
4584
4585    prompt_value.set(true);
4586    assert!(perms.env.check("HOME", None).is_ok());
4587    prompt_value.set(false);
4588    assert!(perms.env.check("HOME", None).is_ok());
4589    assert!(perms.env.check("PATH", None).is_err());
4590
4591    prompt_value.set(true);
4592    assert!(perms.env.check("hostname", None).is_ok());
4593    prompt_value.set(false);
4594    assert!(perms.env.check("hostname", None).is_ok());
4595    assert!(perms.env.check("osRelease", None).is_err());
4596  }
4597
4598  #[test]
4599  fn test_check_fail() {
4600    set_prompter(Box::new(TestPrompter));
4601    let mut perms = Permissions::none_with_prompt();
4602    let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
4603    let parser = TestPermissionDescriptorParser;
4604    let read_query =
4605      |path: &str| parser.parse_path_query(path).unwrap().into_read();
4606    let write_query =
4607      |path: &str| parser.parse_path_query(path).unwrap().into_write();
4608    let ffi_query =
4609      |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
4610
4611    prompt_value.set(false);
4612    assert!(perms.read.check(&read_query("/foo"), None).is_err());
4613    prompt_value.set(true);
4614    assert!(perms.read.check(&read_query("/foo"), None).is_err());
4615    assert!(perms.read.check(&read_query("/bar"), None).is_ok());
4616    prompt_value.set(false);
4617    assert!(perms.read.check(&read_query("/bar"), None).is_ok());
4618
4619    prompt_value.set(false);
4620    assert!(perms.write.check(&write_query("/foo"), None).is_err());
4621    prompt_value.set(true);
4622    assert!(perms.write.check(&write_query("/foo"), None).is_err());
4623    assert!(perms.write.check(&write_query("/bar"), None).is_ok());
4624    prompt_value.set(false);
4625    assert!(perms.write.check(&write_query("/bar"), None).is_ok());
4626
4627    prompt_value.set(false);
4628    assert!(perms.ffi.check(&ffi_query("/foo"), None).is_err());
4629    prompt_value.set(true);
4630    assert!(perms.ffi.check(&ffi_query("/foo"), None).is_err());
4631    assert!(perms.ffi.check(&ffi_query("/bar"), None).is_ok());
4632    prompt_value.set(false);
4633    assert!(perms.ffi.check(&ffi_query("/bar"), None).is_ok());
4634
4635    prompt_value.set(false);
4636    assert!(perms
4637      .net
4638      .check(
4639        &NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)),
4640        None
4641      )
4642      .is_err());
4643    prompt_value.set(true);
4644    assert!(perms
4645      .net
4646      .check(
4647        &NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)),
4648        None
4649      )
4650      .is_err());
4651    assert!(perms
4652      .net
4653      .check(
4654        &NetDescriptor(Host::must_parse("127.0.0.1"), Some(8001)),
4655        None
4656      )
4657      .is_ok());
4658    assert!(perms
4659      .net
4660      .check(
4661        &NetDescriptor(Host::must_parse("deno.land"), Some(8000)),
4662        None
4663      )
4664      .is_ok());
4665    prompt_value.set(false);
4666    assert!(perms
4667      .net
4668      .check(
4669        &NetDescriptor(Host::must_parse("127.0.0.1"), Some(8001)),
4670        None
4671      )
4672      .is_ok());
4673    assert!(perms
4674      .net
4675      .check(
4676        &NetDescriptor(Host::must_parse("deno.land"), Some(8000)),
4677        None
4678      )
4679      .is_ok());
4680
4681    prompt_value.set(false);
4682    #[allow(clippy::disallowed_methods)]
4683    let cwd = std::env::current_dir().unwrap();
4684    assert!(perms
4685      .run
4686      .check(
4687        &RunQueryDescriptor::Path {
4688          requested: "cat".to_string(),
4689          resolved: cwd.join("cat")
4690        },
4691        None
4692      )
4693      .is_err());
4694    prompt_value.set(true);
4695    assert!(perms
4696      .run
4697      .check(
4698        &RunQueryDescriptor::Path {
4699          requested: "cat".to_string(),
4700          resolved: cwd.join("cat")
4701        },
4702        None
4703      )
4704      .is_err());
4705    assert!(perms
4706      .run
4707      .check(
4708        &RunQueryDescriptor::Path {
4709          requested: "ls".to_string(),
4710          resolved: cwd.join("ls")
4711        },
4712        None
4713      )
4714      .is_ok());
4715    prompt_value.set(false);
4716    assert!(perms
4717      .run
4718      .check(
4719        &RunQueryDescriptor::Path {
4720          requested: "ls".to_string(),
4721          resolved: cwd.join("ls")
4722        },
4723        None
4724      )
4725      .is_ok());
4726
4727    prompt_value.set(false);
4728    assert!(perms.env.check("HOME", None).is_err());
4729    prompt_value.set(true);
4730    assert!(perms.env.check("HOME", None).is_err());
4731    assert!(perms.env.check("PATH", None).is_ok());
4732    prompt_value.set(false);
4733    assert!(perms.env.check("PATH", None).is_ok());
4734
4735    prompt_value.set(false);
4736    let sys_desc = |name: &str| SysDescriptor::parse(name.to_string()).unwrap();
4737    assert!(perms.sys.check(&sys_desc("hostname"), None).is_err());
4738    prompt_value.set(true);
4739    assert!(perms.sys.check(&sys_desc("hostname"), None).is_err());
4740    assert!(perms.sys.check(&sys_desc("osRelease"), None).is_ok());
4741    prompt_value.set(false);
4742    assert!(perms.sys.check(&sys_desc("osRelease"), None).is_ok());
4743  }
4744
4745  #[test]
4746  #[cfg(windows)]
4747  fn test_env_windows() {
4748    set_prompter(Box::new(TestPrompter));
4749    let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
4750    let mut perms = Permissions::allow_all();
4751    perms.env = UnaryPermission {
4752      granted_global: false,
4753      ..Permissions::new_unary(
4754        Some(HashSet::from([EnvDescriptor::new("HOME")])),
4755        None,
4756        false,
4757      )
4758    };
4759
4760    prompt_value.set(true);
4761    assert!(perms.env.check("HOME", None).is_ok());
4762    prompt_value.set(false);
4763    assert!(perms.env.check("HOME", None).is_ok());
4764    assert!(perms.env.check("hOmE", None).is_ok());
4765
4766    assert_eq!(perms.env.revoke(Some("HomE")), PermissionState::Prompt);
4767  }
4768
4769  #[test]
4770  fn test_env_wildcards() {
4771    set_prompter(Box::new(TestPrompter));
4772    let _prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
4773    let mut perms = Permissions::allow_all();
4774    perms.env = UnaryPermission {
4775      granted_global: false,
4776      ..Permissions::new_unary(
4777        Some(HashSet::from([EnvDescriptor::new("HOME_*")])),
4778        None,
4779        false,
4780      )
4781    };
4782    assert_eq!(perms.env.query(Some("HOME")), PermissionState::Prompt);
4783    assert_eq!(perms.env.query(Some("HOME_")), PermissionState::Granted);
4784    assert_eq!(perms.env.query(Some("HOME_TEST")), PermissionState::Granted);
4785
4786    // assert no privilege escalation
4787    let parser = TestPermissionDescriptorParser;
4788    assert!(perms
4789      .env
4790      .create_child_permissions(
4791        ChildUnaryPermissionArg::GrantedList(vec!["HOME_SUB".to_string()]),
4792        |value| parser.parse_env_descriptor(value).map(Some),
4793      )
4794      .is_ok());
4795    assert!(perms
4796      .env
4797      .create_child_permissions(
4798        ChildUnaryPermissionArg::GrantedList(vec!["HOME*".to_string()]),
4799        |value| parser.parse_env_descriptor(value).map(Some),
4800      )
4801      .is_err());
4802    assert!(perms
4803      .env
4804      .create_child_permissions(
4805        ChildUnaryPermissionArg::GrantedList(vec!["OUTSIDE".to_string()]),
4806        |value| parser.parse_env_descriptor(value).map(Some),
4807      )
4808      .is_err());
4809    assert!(perms
4810      .env
4811      .create_child_permissions(
4812        // ok because this is a subset of HOME_*
4813        ChildUnaryPermissionArg::GrantedList(vec!["HOME_S*".to_string()]),
4814        |value| parser.parse_env_descriptor(value).map(Some),
4815      )
4816      .is_ok());
4817  }
4818
4819  #[test]
4820  fn test_check_partial_denied() {
4821    let parser = TestPermissionDescriptorParser;
4822    let mut perms = Permissions::from_options(
4823      &parser,
4824      &PermissionsOptions {
4825        allow_read: Some(vec![]),
4826        deny_read: Some(svec!["/foo/bar"]),
4827        allow_write: Some(vec![]),
4828        deny_write: Some(svec!["/foo/bar"]),
4829        ..Default::default()
4830      },
4831    )
4832    .unwrap();
4833
4834    let read_query = parser.parse_path_query("/foo").unwrap().into_read();
4835    perms.read.check_partial(&read_query, None).unwrap();
4836    assert!(perms.read.check(&read_query, None).is_err());
4837
4838    let write_query = parser.parse_path_query("/foo").unwrap().into_write();
4839    perms.write.check_partial(&write_query, None).unwrap();
4840    assert!(perms.write.check(&write_query, None).is_err());
4841  }
4842
4843  #[test]
4844  fn test_net_fully_qualified_domain_name() {
4845    set_prompter(Box::new(TestPrompter));
4846    let parser = TestPermissionDescriptorParser;
4847    let perms = Permissions::from_options(
4848      &parser,
4849      &PermissionsOptions {
4850        allow_net: Some(svec!["allowed.domain", "1.1.1.1"]),
4851        deny_net: Some(svec!["denied.domain", "2.2.2.2"]),
4852        ..Default::default()
4853      },
4854    )
4855    .unwrap();
4856    let mut perms = PermissionsContainer::new(Arc::new(parser), perms);
4857    let cases = [
4858      ("allowed.domain.", true),
4859      ("1.1.1.1", true),
4860      ("denied.domain.", false),
4861      ("2.2.2.2", false),
4862    ];
4863
4864    for (host, is_ok) in cases {
4865      assert_eq!(perms.check_net(&(host, None), "api").is_ok(), is_ok);
4866    }
4867  }
4868
4869  #[test]
4870  fn test_deserialize_child_permissions_arg() {
4871    set_prompter(Box::new(TestPrompter));
4872    assert_eq!(
4873      ChildPermissionsArg::inherit(),
4874      ChildPermissionsArg {
4875        env: ChildUnaryPermissionArg::Inherit,
4876        net: ChildUnaryPermissionArg::Inherit,
4877        ffi: ChildUnaryPermissionArg::Inherit,
4878        import: ChildUnaryPermissionArg::Inherit,
4879        read: ChildUnaryPermissionArg::Inherit,
4880        run: ChildUnaryPermissionArg::Inherit,
4881        sys: ChildUnaryPermissionArg::Inherit,
4882        write: ChildUnaryPermissionArg::Inherit,
4883      }
4884    );
4885    assert_eq!(
4886      ChildPermissionsArg::none(),
4887      ChildPermissionsArg {
4888        env: ChildUnaryPermissionArg::NotGranted,
4889        net: ChildUnaryPermissionArg::NotGranted,
4890        ffi: ChildUnaryPermissionArg::NotGranted,
4891        import: ChildUnaryPermissionArg::NotGranted,
4892        read: ChildUnaryPermissionArg::NotGranted,
4893        run: ChildUnaryPermissionArg::NotGranted,
4894        sys: ChildUnaryPermissionArg::NotGranted,
4895        write: ChildUnaryPermissionArg::NotGranted,
4896      }
4897    );
4898    assert_eq!(
4899      serde_json::from_value::<ChildPermissionsArg>(json!("inherit")).unwrap(),
4900      ChildPermissionsArg::inherit()
4901    );
4902    assert_eq!(
4903      serde_json::from_value::<ChildPermissionsArg>(json!("none")).unwrap(),
4904      ChildPermissionsArg::none()
4905    );
4906    assert_eq!(
4907      serde_json::from_value::<ChildPermissionsArg>(json!({})).unwrap(),
4908      ChildPermissionsArg::none()
4909    );
4910    assert_eq!(
4911      serde_json::from_value::<ChildPermissionsArg>(json!({
4912        "env": ["foo", "bar"],
4913      }))
4914      .unwrap(),
4915      ChildPermissionsArg {
4916        env: ChildUnaryPermissionArg::GrantedList(svec!["foo", "bar"]),
4917        ..ChildPermissionsArg::none()
4918      }
4919    );
4920    assert_eq!(
4921      serde_json::from_value::<ChildPermissionsArg>(json!({
4922        "env": true,
4923        "net": true,
4924        "ffi": true,
4925        "import": true,
4926        "read": true,
4927        "run": true,
4928        "sys": true,
4929        "write": true,
4930      }))
4931      .unwrap(),
4932      ChildPermissionsArg {
4933        env: ChildUnaryPermissionArg::Granted,
4934        net: ChildUnaryPermissionArg::Granted,
4935        ffi: ChildUnaryPermissionArg::Granted,
4936        import: ChildUnaryPermissionArg::Granted,
4937        read: ChildUnaryPermissionArg::Granted,
4938        run: ChildUnaryPermissionArg::Granted,
4939        sys: ChildUnaryPermissionArg::Granted,
4940        write: ChildUnaryPermissionArg::Granted,
4941      }
4942    );
4943    assert_eq!(
4944      serde_json::from_value::<ChildPermissionsArg>(json!({
4945        "env": false,
4946        "net": false,
4947        "ffi": false,
4948        "import": false,
4949        "read": false,
4950        "run": false,
4951        "sys": false,
4952        "write": false,
4953      }))
4954      .unwrap(),
4955      ChildPermissionsArg {
4956        env: ChildUnaryPermissionArg::NotGranted,
4957        net: ChildUnaryPermissionArg::NotGranted,
4958        ffi: ChildUnaryPermissionArg::NotGranted,
4959        import: ChildUnaryPermissionArg::NotGranted,
4960        read: ChildUnaryPermissionArg::NotGranted,
4961        run: ChildUnaryPermissionArg::NotGranted,
4962        sys: ChildUnaryPermissionArg::NotGranted,
4963        write: ChildUnaryPermissionArg::NotGranted,
4964      }
4965    );
4966    assert_eq!(
4967      serde_json::from_value::<ChildPermissionsArg>(json!({
4968        "env": ["foo", "bar"],
4969        "net": ["foo", "bar:8000"],
4970        "ffi": ["foo", "file:///bar/baz"],
4971        "import": ["example.com"],
4972        "read": ["foo", "file:///bar/baz"],
4973        "run": ["foo", "file:///bar/baz", "./qux"],
4974        "sys": ["hostname", "osRelease"],
4975        "write": ["foo", "file:///bar/baz"],
4976      }))
4977      .unwrap(),
4978      ChildPermissionsArg {
4979        env: ChildUnaryPermissionArg::GrantedList(svec!["foo", "bar"]),
4980        net: ChildUnaryPermissionArg::GrantedList(svec!["foo", "bar:8000"]),
4981        ffi: ChildUnaryPermissionArg::GrantedList(svec![
4982          "foo",
4983          "file:///bar/baz"
4984        ]),
4985        import: ChildUnaryPermissionArg::GrantedList(svec!["example.com"]),
4986        read: ChildUnaryPermissionArg::GrantedList(svec![
4987          "foo",
4988          "file:///bar/baz"
4989        ]),
4990        run: ChildUnaryPermissionArg::GrantedList(svec![
4991          "foo",
4992          "file:///bar/baz",
4993          "./qux"
4994        ]),
4995        sys: ChildUnaryPermissionArg::GrantedList(svec![
4996          "hostname",
4997          "osRelease"
4998        ]),
4999        write: ChildUnaryPermissionArg::GrantedList(svec![
5000          "foo",
5001          "file:///bar/baz"
5002        ]),
5003      }
5004    );
5005  }
5006
5007  #[test]
5008  fn test_create_child_permissions() {
5009    set_prompter(Box::new(TestPrompter));
5010    let parser = TestPermissionDescriptorParser;
5011    let main_perms = Permissions::from_options(
5012      &parser,
5013      &PermissionsOptions {
5014        allow_env: Some(vec![]),
5015        allow_net: Some(svec!["foo", "bar"]),
5016        ..Default::default()
5017      },
5018    )
5019    .unwrap();
5020    let main_perms = PermissionsContainer::new(Arc::new(parser), main_perms);
5021    assert_eq!(
5022      main_perms
5023        .create_child_permissions(ChildPermissionsArg {
5024          env: ChildUnaryPermissionArg::Inherit,
5025          net: ChildUnaryPermissionArg::GrantedList(svec!["foo"]),
5026          ffi: ChildUnaryPermissionArg::NotGranted,
5027          ..ChildPermissionsArg::none()
5028        })
5029        .unwrap()
5030        .inner
5031        .lock()
5032        .clone(),
5033      Permissions {
5034        env: Permissions::new_unary(Some(HashSet::new()), None, false),
5035        net: Permissions::new_unary(
5036          Some(HashSet::from([NetDescriptor::parse("foo").unwrap()])),
5037          None,
5038          false
5039        ),
5040        ..Permissions::none_without_prompt()
5041      }
5042    );
5043    assert!(main_perms
5044      .create_child_permissions(ChildPermissionsArg {
5045        net: ChildUnaryPermissionArg::Granted,
5046        ..ChildPermissionsArg::none()
5047      })
5048      .is_err());
5049    assert!(main_perms
5050      .create_child_permissions(ChildPermissionsArg {
5051        net: ChildUnaryPermissionArg::GrantedList(svec!["foo", "bar", "baz"]),
5052        ..ChildPermissionsArg::none()
5053      })
5054      .is_err());
5055    assert!(main_perms
5056      .create_child_permissions(ChildPermissionsArg {
5057        ffi: ChildUnaryPermissionArg::GrantedList(svec!["foo"]),
5058        ..ChildPermissionsArg::none()
5059      })
5060      .is_err());
5061  }
5062
5063  #[test]
5064  fn test_create_child_permissions_with_prompt() {
5065    set_prompter(Box::new(TestPrompter));
5066    let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
5067    let main_perms = Permissions::from_options(
5068      &TestPermissionDescriptorParser,
5069      &PermissionsOptions {
5070        prompt: true,
5071        ..Default::default()
5072      },
5073    )
5074    .unwrap();
5075    let main_perms = PermissionsContainer::new(
5076      Arc::new(TestPermissionDescriptorParser),
5077      main_perms,
5078    );
5079    prompt_value.set(true);
5080    let worker_perms = main_perms
5081      .create_child_permissions(ChildPermissionsArg {
5082        read: ChildUnaryPermissionArg::Granted,
5083        run: ChildUnaryPermissionArg::GrantedList(svec!["foo", "bar"]),
5084        ..ChildPermissionsArg::none()
5085      })
5086      .unwrap();
5087    assert_eq!(
5088      main_perms.inner.lock().clone(),
5089      worker_perms.inner.lock().clone()
5090    );
5091    assert_eq!(
5092      main_perms.inner.lock().run.granted_list,
5093      HashSet::from([
5094        AllowRunDescriptor(PathBuf::from("/bar")),
5095        AllowRunDescriptor(PathBuf::from("/foo")),
5096      ])
5097    );
5098  }
5099
5100  #[test]
5101  fn test_create_child_permissions_with_inherited_denied_list() {
5102    set_prompter(Box::new(TestPrompter));
5103    let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
5104    let parser = TestPermissionDescriptorParser;
5105    let main_perms = Permissions::from_options(
5106      &parser,
5107      &PermissionsOptions {
5108        prompt: true,
5109        ..Default::default()
5110      },
5111    )
5112    .unwrap();
5113    let main_perms =
5114      PermissionsContainer::new(Arc::new(parser.clone()), main_perms);
5115    prompt_value.set(false);
5116    assert!(main_perms
5117      .inner
5118      .lock()
5119      .write
5120      .check(&parser.parse_path_query("foo").unwrap().into_write(), None)
5121      .is_err());
5122    let worker_perms = main_perms
5123      .create_child_permissions(ChildPermissionsArg::none())
5124      .unwrap();
5125    assert_eq!(
5126      worker_perms.inner.lock().write.flag_denied_list.clone(),
5127      main_perms.inner.lock().write.flag_denied_list
5128    );
5129  }
5130
5131  #[test]
5132  fn test_host_parse() {
5133    let hosts = &[
5134      ("deno.land", Some(Host::Fqdn(fqdn!("deno.land")))),
5135      ("DENO.land", Some(Host::Fqdn(fqdn!("deno.land")))),
5136      ("deno.land.", Some(Host::Fqdn(fqdn!("deno.land")))),
5137      (
5138        "1.1.1.1",
5139        Some(Host::Ip(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)))),
5140      ),
5141      (
5142        "::1",
5143        Some(Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))),
5144      ),
5145      (
5146        "[::1]",
5147        Some(Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))),
5148      ),
5149      ("[::1", None),
5150      ("::1]", None),
5151      ("deno. land", None),
5152      ("1. 1.1.1", None),
5153      ("1.1.1.1.", None),
5154      ("1::1.", None),
5155      ("deno.land.", Some(Host::Fqdn(fqdn!("deno.land")))),
5156      (".deno.land", None),
5157      (
5158        "::ffff:1.1.1.1",
5159        Some(Host::Ip(IpAddr::V6(Ipv6Addr::new(
5160          0, 0, 0, 0, 0, 0xffff, 0x0101, 0x0101,
5161        )))),
5162      ),
5163    ];
5164
5165    for (host_str, expected) in hosts {
5166      assert_eq!(Host::parse(host_str).ok(), *expected, "{host_str}");
5167    }
5168  }
5169
5170  #[test]
5171  fn test_net_descriptor_parse() {
5172    let cases = &[
5173      (
5174        "deno.land",
5175        Some(NetDescriptor(Host::Fqdn(fqdn!("deno.land")), None)),
5176      ),
5177      (
5178        "DENO.land",
5179        Some(NetDescriptor(Host::Fqdn(fqdn!("deno.land")), None)),
5180      ),
5181      (
5182        "deno.land:8000",
5183        Some(NetDescriptor(Host::Fqdn(fqdn!("deno.land")), Some(8000))),
5184      ),
5185      ("deno.land:", None),
5186      ("deno.land:a", None),
5187      ("deno. land:a", None),
5188      ("deno.land.: a", None),
5189      (
5190        "1.1.1.1",
5191        Some(NetDescriptor(
5192          Host::Ip(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1))),
5193          None,
5194        )),
5195      ),
5196      ("1.1.1.1.", None),
5197      ("1.1.1.1..", None),
5198      (
5199        "1.1.1.1:8000",
5200        Some(NetDescriptor(
5201          Host::Ip(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1))),
5202          Some(8000),
5203        )),
5204      ),
5205      ("::", None),
5206      (":::80", None),
5207      ("::80", None),
5208      (
5209        "[::]",
5210        Some(NetDescriptor(
5211          Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0))),
5212          None,
5213        )),
5214      ),
5215      ("[::1", None),
5216      ("::1]", None),
5217      ("::1]", None),
5218      ("[::1]:", None),
5219      ("[::1]:a", None),
5220      (
5221        "[::1]:443",
5222        Some(NetDescriptor(
5223          Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
5224          Some(443),
5225        )),
5226      ),
5227      ("", None),
5228      ("deno.land..", None),
5229    ];
5230
5231    for (input, expected) in cases {
5232      assert_eq!(NetDescriptor::parse(input).ok(), *expected, "'{input}'");
5233    }
5234  }
5235
5236  #[test]
5237  fn test_denies_run_name() {
5238    let cases = [
5239      #[cfg(windows)]
5240      ("deno", "C:\\deno.exe", true),
5241      #[cfg(windows)]
5242      ("deno", "C:\\sub\\deno.cmd", true),
5243      #[cfg(windows)]
5244      ("deno", "C:\\sub\\DeNO.cmd", true),
5245      #[cfg(windows)]
5246      ("DEno", "C:\\sub\\deno.cmd", true),
5247      #[cfg(windows)]
5248      ("deno", "C:\\other\\sub\\deno.batch", true),
5249      #[cfg(windows)]
5250      ("deno", "C:\\other\\sub\\deno", true),
5251      #[cfg(windows)]
5252      ("denort", "C:\\other\\sub\\deno.exe", false),
5253      ("deno", "/home/test/deno", true),
5254      ("deno", "/home/test/denot", false),
5255    ];
5256    for (name, cmd_path, denies) in cases {
5257      assert_eq!(
5258        denies_run_name(name, &PathBuf::from(cmd_path)),
5259        denies,
5260        "{} {}",
5261        name,
5262        cmd_path
5263      );
5264    }
5265  }
5266}