1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

refactor: improve node permission checks (#26028)

Does less work when requesting permissions with `-A`
This commit is contained in:
David Sherret 2024-10-04 20:55:41 +01:00 committed by GitHub
parent f288730c38
commit 2de4faa483
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 207 additions and 121 deletions

View file

@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
@ -32,18 +33,19 @@ pub type CliByonmNpmResolver = ByonmNpmResolver<CliDenoResolverFs>;
struct CliByonmWrapper(Arc<CliByonmNpmResolver>); struct CliByonmWrapper(Arc<CliByonmNpmResolver>);
impl NodeRequireResolver for CliByonmWrapper { impl NodeRequireResolver for CliByonmWrapper {
fn ensure_read_permission( fn ensure_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
if !path if !path
.components() .components()
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules") .any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
{ {
_ = permissions.check_read_path(path)?; permissions.check_read_path(path)
} else {
Ok(Cow::Borrowed(path))
} }
Ok(())
} }
} }

View file

@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
@ -593,11 +594,11 @@ impl NpmResolver for ManagedCliNpmResolver {
} }
impl NodeRequireResolver for ManagedCliNpmResolver { impl NodeRequireResolver for ManagedCliNpmResolver {
fn ensure_read_permission( fn ensure_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
self.fs_resolver.ensure_read_permission(permissions, path) self.fs_resolver.ensure_read_permission(permissions, path)
} }
} }

View file

@ -3,6 +3,7 @@
pub mod bin_entries; pub mod bin_entries;
pub mod lifecycle_scripts; pub mod lifecycle_scripts;
use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::Path; use std::path::Path;
@ -62,11 +63,12 @@ pub trait NpmPackageFsResolver: Send + Sync {
async fn cache_packages(&self) -> Result<(), AnyError>; async fn cache_packages(&self) -> Result<(), AnyError>;
fn ensure_read_permission( #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError>; ) -> Result<Cow<'a, Path>, AnyError>;
} }
#[derive(Debug)] #[derive(Debug)]
@ -85,11 +87,15 @@ impl RegistryReadPermissionChecker {
} }
} }
pub fn ensure_registry_read_permission( pub fn ensure_registry_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
if permissions.query_read_all() {
return Ok(Cow::Borrowed(path)); // skip permissions checks below
}
// allow reading if it's in the node_modules // allow reading if it's in the node_modules
let is_path_in_node_modules = path.starts_with(&self.registry_path) let is_path_in_node_modules = path.starts_with(&self.registry_path)
&& path && path
@ -118,20 +124,20 @@ impl RegistryReadPermissionChecker {
}, },
} }
}; };
let Some(registry_path_canon) = canonicalize(&self.registry_path)? else { if let Some(registry_path_canon) = canonicalize(&self.registry_path)? {
return Ok(()); // not exists, allow reading if let Some(path_canon) = canonicalize(path)? {
};
let Some(path_canon) = canonicalize(path)? else {
return Ok(()); // not exists, allow reading
};
if path_canon.starts_with(registry_path_canon) { if path_canon.starts_with(registry_path_canon) {
return Ok(()); return Ok(Cow::Owned(path_canon));
}
} else if path.starts_with(registry_path_canon)
|| path.starts_with(&self.registry_path)
{
return Ok(Cow::Borrowed(path));
}
} }
} }
_ = permissions.check_read_path(path)?; permissions.check_read_path(path)
Ok(())
} }
} }

View file

@ -183,11 +183,11 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
Ok(()) Ok(())
} }
fn ensure_read_permission( fn ensure_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
self self
.registry_read_permission_checker .registry_read_permission_checker
.ensure_registry_read_permission(permissions, path) .ensure_registry_read_permission(permissions, path)

View file

@ -257,11 +257,11 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
.await .await
} }
fn ensure_read_permission( fn ensure_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
self self
.registry_read_permission_checker .registry_read_permission_checker
.ensure_registry_read_permission(permissions, path) .ensure_registry_read_permission(permissions, path)

View file

@ -66,6 +66,7 @@ pub trait NodePermissions {
&mut self, &mut self,
path: &'a Path, path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError>; ) -> Result<Cow<'a, Path>, AnyError>;
fn query_read_all(&mut self) -> bool;
fn check_sys(&mut self, kind: &str, api_name: &str) -> Result<(), AnyError>; fn check_sys(&mut self, kind: &str, api_name: &str) -> Result<(), AnyError>;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_write_with_api_name( fn check_write_with_api_name(
@ -103,6 +104,10 @@ impl NodePermissions for deno_permissions::PermissionsContainer {
deno_permissions::PermissionsContainer::check_read_path(self, path, None) deno_permissions::PermissionsContainer::check_read_path(self, path, None)
} }
fn query_read_all(&mut self) -> bool {
deno_permissions::PermissionsContainer::query_read_all(self)
}
#[inline(always)] #[inline(always)]
fn check_write_with_api_name( fn check_write_with_api_name(
&mut self, &mut self,
@ -124,11 +129,12 @@ pub type NodeRequireResolverRc =
deno_fs::sync::MaybeArc<dyn NodeRequireResolver>; deno_fs::sync::MaybeArc<dyn NodeRequireResolver>;
pub trait NodeRequireResolver: std::fmt::Debug + MaybeSend + MaybeSync { pub trait NodeRequireResolver: std::fmt::Debug + MaybeSend + MaybeSync {
fn ensure_read_permission( #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError>; ) -> Result<Cow<'a, Path>, AnyError>;
} }
pub static NODE_ENV_VAR_ALLOWLIST: Lazy<HashSet<String>> = Lazy::new(|| { pub static NODE_ENV_VAR_ALLOWLIST: Lazy<HashSet<String>> = Lazy::new(|| {

View file

@ -15,6 +15,7 @@ use deno_path_util::normalize_path;
use node_resolver::NodeModuleKind; use node_resolver::NodeModuleKind;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
use node_resolver::REQUIRE_CONDITIONS; use node_resolver::REQUIRE_CONDITIONS;
use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
@ -25,10 +26,11 @@ use crate::NodeRequireResolverRc;
use crate::NodeResolverRc; use crate::NodeResolverRc;
use crate::NpmResolverRc; use crate::NpmResolverRc;
fn ensure_read_permission<P>( #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a, P>(
state: &mut OpState, state: &mut OpState,
file_path: &Path, file_path: &'a Path,
) -> Result<(), AnyError> ) -> Result<Cow<'a, Path>, AnyError>
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
@ -107,7 +109,7 @@ where
deno_path_util::normalize_path(current_dir.join(from)) deno_path_util::normalize_path(current_dir.join(from))
}; };
ensure_read_permission::<P>(state, &from)?; let from = ensure_read_permission::<P>(state, &from)?;
if cfg!(windows) { if cfg!(windows) {
// return root node_modules when path is 'D:\\'. // return root node_modules when path is 'D:\\'.
@ -129,7 +131,7 @@ where
} }
let mut paths = Vec::with_capacity(from.components().count()); let mut paths = Vec::with_capacity(from.components().count());
let mut current_path = from.as_path(); let mut current_path = from.as_ref();
let mut maybe_parent = Some(current_path); let mut maybe_parent = Some(current_path);
while let Some(parent) = maybe_parent { while let Some(parent) = maybe_parent {
if !parent.ends_with("node_modules") { if !parent.ends_with("node_modules") {
@ -267,7 +269,7 @@ where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = PathBuf::from(path); let path = PathBuf::from(path);
ensure_read_permission::<P>(state, &path)?; let path = ensure_read_permission::<P>(state, &path)?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
if let Ok(metadata) = fs.stat_sync(&path) { if let Ok(metadata) = fs.stat_sync(&path) {
if metadata.is_file { if metadata.is_file {
@ -290,7 +292,7 @@ where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = PathBuf::from(request); let path = PathBuf::from(request);
ensure_read_permission::<P>(state, &path)?; let path = ensure_read_permission::<P>(state, &path)?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
let canonicalized_path = let canonicalized_path =
deno_core::strip_unc_prefix(fs.realpath_sync(&path)?); deno_core::strip_unc_prefix(fs.realpath_sync(&path)?);
@ -362,7 +364,7 @@ where
if parent_id == "<repl>" || parent_id == "internal/preload" { if parent_id == "<repl>" || parent_id == "internal/preload" {
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
if let Ok(cwd) = fs.cwd() { if let Ok(cwd) = fs.cwd() {
ensure_read_permission::<P>(state, &cwd)?; let cwd = ensure_read_permission::<P>(state, &cwd)?;
return Ok(Some(cwd.to_string_lossy().into_owned())); return Ok(Some(cwd.to_string_lossy().into_owned()));
} }
} }
@ -443,7 +445,7 @@ where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let file_path = PathBuf::from(file_path); let file_path = PathBuf::from(file_path);
ensure_read_permission::<P>(state, &file_path)?; let file_path = ensure_read_permission::<P>(state, &file_path)?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
Ok(fs.read_text_file_lossy_sync(&file_path, None)?) Ok(fs.read_text_file_lossy_sync(&file_path, None)?)
} }
@ -528,7 +530,7 @@ where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let filename = PathBuf::from(filename); let filename = PathBuf::from(filename);
ensure_read_permission::<P>(state, filename.parent().unwrap())?; // permissions: allow reading the closest package.json files
let node_resolver = state.borrow::<NodeResolverRc>().clone(); let node_resolver = state.borrow::<NodeResolverRc>().clone();
node_resolver node_resolver
.get_closest_package_json_from_path(&filename) .get_closest_package_json_from_path(&filename)
@ -567,7 +569,7 @@ where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let referrer_path = PathBuf::from(&referrer_filename); let referrer_path = PathBuf::from(&referrer_filename);
ensure_read_permission::<P>(state, &referrer_path)?; let referrer_path = ensure_read_permission::<P>(state, &referrer_path)?;
let node_resolver = state.borrow::<NodeResolverRc>(); let node_resolver = state.borrow::<NodeResolverRc>();
let Some(pkg) = let Some(pkg) =
node_resolver.get_closest_package_json_from_path(&referrer_path)? node_resolver.get_closest_package_json_from_path(&referrer_path)?

View file

@ -7,6 +7,7 @@ use deno_core::url::Url;
use deno_core::OpState; use deno_core::OpState;
use deno_fs::FileSystemRc; use deno_fs::FileSystemRc;
use node_resolver::NodeResolution; use node_resolver::NodeResolution;
use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
@ -14,10 +15,11 @@ use crate::NodePermissions;
use crate::NodeRequireResolverRc; use crate::NodeRequireResolverRc;
use crate::NodeResolverRc; use crate::NodeResolverRc;
fn ensure_read_permission<P>( #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a, P>(
state: &mut OpState, state: &mut OpState,
file_path: &Path, file_path: &'a Path,
) -> Result<(), AnyError> ) -> Result<Cow<'a, Path>, AnyError>
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
@ -47,7 +49,7 @@ where
"Relative path entries must start with '.' or '..'", "Relative path entries must start with '.' or '..'",
)); ));
} }
ensure_read_permission::<P>(state, &path)?; let path = ensure_read_permission::<P>(state, &path)?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
let canonicalized_path = let canonicalized_path =
deno_core::strip_unc_prefix(fs.realpath_sync(&path)?); deno_core::strip_unc_prefix(fs.realpath_sync(&path)?);
@ -57,7 +59,7 @@ where
let url_path = url let url_path = url
.to_file_path() .to_file_path()
.map_err(|e| generic_error(format!("URL to Path-String: {:#?}", e)))?; .map_err(|e| generic_error(format!("URL to Path-String: {:#?}", e)))?;
ensure_read_permission::<P>(state, &url_path)?; let url_path = ensure_read_permission::<P>(state, &url_path)?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
if !fs.exists_sync(&url_path) { if !fs.exists_sync(&url_path) {
return Err(generic_error(format!("File not found [{:?}]", url_path))); return Err(generic_error(format!("File not found [{:?}]", url_path)));

View file

@ -2285,6 +2285,11 @@ impl PermissionsContainer {
self.inner.lock().read.check_all(Some(api_name)) self.inner.lock().read.check_all(Some(api_name))
} }
#[inline(always)]
pub fn query_read_all(&self) -> bool {
self.inner.lock().read.query(None) == PermissionState::Granted
}
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)] #[inline(always)]
pub fn check_write( pub fn check_write(
@ -2614,8 +2619,13 @@ impl PermissionsContainer {
&self, &self,
path: Option<&str>, path: Option<&str>,
) -> Result<PermissionState, AnyError> { ) -> Result<PermissionState, AnyError> {
let inner = self.inner.lock();
let permission = &inner.read;
if permission.is_allow_all() {
return Ok(PermissionState::Granted);
}
Ok( Ok(
self.inner.lock().read.query( permission.query(
path path
.map(|path| { .map(|path| {
Result::<_, AnyError>::Ok( Result::<_, AnyError>::Ok(
@ -2633,8 +2643,13 @@ impl PermissionsContainer {
&self, &self,
path: Option<&str>, path: Option<&str>,
) -> Result<PermissionState, AnyError> { ) -> Result<PermissionState, AnyError> {
let inner = self.inner.lock();
let permission = &inner.write;
if permission.is_allow_all() {
return Ok(PermissionState::Granted);
}
Ok( Ok(
self.inner.lock().write.query( permission.query(
path path
.map(|path| { .map(|path| {
Result::<_, AnyError>::Ok( Result::<_, AnyError>::Ok(
@ -2652,8 +2667,13 @@ impl PermissionsContainer {
&self, &self,
host: Option<&str>, host: Option<&str>,
) -> Result<PermissionState, AnyError> { ) -> Result<PermissionState, AnyError> {
let inner = self.inner.lock();
let permission = &inner.net;
if permission.is_allow_all() {
return Ok(PermissionState::Granted);
}
Ok( Ok(
self.inner.lock().net.query( permission.query(
match host { match host {
None => None, None => None,
Some(h) => Some(self.descriptor_parser.parse_net_descriptor(h)?), Some(h) => Some(self.descriptor_parser.parse_net_descriptor(h)?),
@ -2665,7 +2685,12 @@ impl PermissionsContainer {
#[inline(always)] #[inline(always)]
pub fn query_env(&self, var: Option<&str>) -> PermissionState { pub fn query_env(&self, var: Option<&str>) -> PermissionState {
self.inner.lock().env.query(var) let inner = self.inner.lock();
let permission = &inner.env;
if permission.is_allow_all() {
return PermissionState::Granted;
}
permission.query(var)
} }
#[inline(always)] #[inline(always)]
@ -2673,8 +2698,13 @@ impl PermissionsContainer {
&self, &self,
kind: Option<&str>, kind: Option<&str>,
) -> Result<PermissionState, AnyError> { ) -> Result<PermissionState, AnyError> {
let inner = self.inner.lock();
let permission = &inner.sys;
if permission.is_allow_all() {
return Ok(PermissionState::Granted);
}
Ok( Ok(
self.inner.lock().sys.query( permission.query(
kind kind
.map(|kind| self.descriptor_parser.parse_sys_descriptor(kind)) .map(|kind| self.descriptor_parser.parse_sys_descriptor(kind))
.transpose()? .transpose()?
@ -2688,8 +2718,13 @@ impl PermissionsContainer {
&self, &self,
cmd: Option<&str>, cmd: Option<&str>,
) -> Result<PermissionState, AnyError> { ) -> Result<PermissionState, AnyError> {
let inner = self.inner.lock();
let permission = &inner.run;
if permission.is_allow_all() {
return Ok(PermissionState::Granted);
}
Ok( Ok(
self.inner.lock().run.query( permission.query(
cmd cmd
.map(|request| self.descriptor_parser.parse_run_query(request)) .map(|request| self.descriptor_parser.parse_run_query(request))
.transpose()? .transpose()?
@ -2703,8 +2738,13 @@ impl PermissionsContainer {
&self, &self,
path: Option<&str>, path: Option<&str>,
) -> Result<PermissionState, AnyError> { ) -> Result<PermissionState, AnyError> {
let inner = self.inner.lock();
let permission = &inner.ffi;
if permission.is_allow_all() {
return Ok(PermissionState::Granted);
}
Ok( Ok(
self.inner.lock().ffi.query( permission.query(
path path
.map(|path| { .map(|path| {
Result::<_, AnyError>::Ok( Result::<_, AnyError>::Ok(

View file

@ -97,6 +97,9 @@ impl deno_node::NodePermissions for Permissions {
) -> Result<PathBuf, deno_core::error::AnyError> { ) -> Result<PathBuf, deno_core::error::AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
fn query_read_all(&mut self) -> bool {
unreachable!("snapshotting!")
}
fn check_write_with_api_name( fn check_write_with_api_name(
&mut self, &mut self,
_p: &str, _p: &str,

View file

@ -20,19 +20,27 @@ Deno.test(function permissionInvalidNameSync() {
}, TypeError); }, TypeError);
}); });
Deno.test(async function permissionNetInvalidHost() { Deno.test(
{ permissions: { net: [] } },
async function permissionNetInvalidHost() {
await assertRejects(async () => { await assertRejects(async () => {
await Deno.permissions.query({ name: "net", host: ":" }); await Deno.permissions.query({ name: "net", host: ":" });
}, URIError); }, URIError);
}); },
);
Deno.test(function permissionNetInvalidHostSync() { Deno.test(
{ permissions: { net: [] } },
function permissionNetInvalidHostSync() {
assertThrows(() => { assertThrows(() => {
Deno.permissions.querySync({ name: "net", host: ":" }); Deno.permissions.querySync({ name: "net", host: ":" });
}, URIError); }, URIError);
}); },
);
Deno.test(async function permissionSysValidKind() { Deno.test(
{ permissions: { sys: [] } },
async function permissionSysValidKind() {
await Deno.permissions.query({ name: "sys", kind: "loadavg" }); await Deno.permissions.query({ name: "sys", kind: "loadavg" });
await Deno.permissions.query({ name: "sys", kind: "osRelease" }); await Deno.permissions.query({ name: "sys", kind: "osRelease" });
await Deno.permissions.query({ name: "sys", kind: "osUptime" }); await Deno.permissions.query({ name: "sys", kind: "osUptime" });
@ -42,9 +50,12 @@ Deno.test(async function permissionSysValidKind() {
await Deno.permissions.query({ name: "sys", kind: "uid" }); await Deno.permissions.query({ name: "sys", kind: "uid" });
await Deno.permissions.query({ name: "sys", kind: "gid" }); await Deno.permissions.query({ name: "sys", kind: "gid" });
await Deno.permissions.query({ name: "sys", kind: "cpus" }); await Deno.permissions.query({ name: "sys", kind: "cpus" });
}); },
);
Deno.test(function permissionSysValidKindSync() { Deno.test(
{ permissions: { sys: [] } },
function permissionSysValidKindSync() {
Deno.permissions.querySync({ name: "sys", kind: "loadavg" }); Deno.permissions.querySync({ name: "sys", kind: "loadavg" });
Deno.permissions.querySync({ name: "sys", kind: "osRelease" }); Deno.permissions.querySync({ name: "sys", kind: "osRelease" });
Deno.permissions.querySync({ name: "sys", kind: "networkInterfaces" }); Deno.permissions.querySync({ name: "sys", kind: "networkInterfaces" });
@ -53,21 +64,28 @@ Deno.test(function permissionSysValidKindSync() {
Deno.permissions.querySync({ name: "sys", kind: "uid" }); Deno.permissions.querySync({ name: "sys", kind: "uid" });
Deno.permissions.querySync({ name: "sys", kind: "gid" }); Deno.permissions.querySync({ name: "sys", kind: "gid" });
Deno.permissions.querySync({ name: "sys", kind: "cpus" }); Deno.permissions.querySync({ name: "sys", kind: "cpus" });
}); },
);
Deno.test(async function permissionSysInvalidKind() { Deno.test(
{ permissions: { sys: [] } },
async function permissionSysInvalidKind() {
await assertRejects(async () => { await assertRejects(async () => {
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
await Deno.permissions.query({ name: "sys", kind: "abc" as any }); await Deno.permissions.query({ name: "sys", kind: "abc" as any });
}, TypeError); }, TypeError);
}); },
);
Deno.test(function permissionSysInvalidKindSync() { Deno.test(
{ permissions: { sys: [] } },
function permissionSysInvalidKindSync() {
assertThrows(() => { assertThrows(() => {
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
Deno.permissions.querySync({ name: "sys", kind: "abc" as any }); Deno.permissions.querySync({ name: "sys", kind: "abc" as any });
}, TypeError); }, TypeError);
}); },
);
Deno.test(async function permissionQueryReturnsEventTarget() { Deno.test(async function permissionQueryReturnsEventTarget() {
const status = await Deno.permissions.query({ name: "read", path: "." }); const status = await Deno.permissions.query({ name: "read", path: "." });
@ -134,16 +152,21 @@ Deno.test(function permissionStatusIllegalConstructor() {
}); });
// Regression test for https://github.com/denoland/deno/issues/17020 // Regression test for https://github.com/denoland/deno/issues/17020
Deno.test(async function permissionURL() { Deno.test(
{ permissions: { read: [], write: [], ffi: [], run: [] } },
async function permissionURL() {
const path = new URL(".", import.meta.url); const path = new URL(".", import.meta.url);
await Deno.permissions.query({ name: "read", path }); await Deno.permissions.query({ name: "read", path });
await Deno.permissions.query({ name: "write", path }); await Deno.permissions.query({ name: "write", path });
await Deno.permissions.query({ name: "ffi", path }); await Deno.permissions.query({ name: "ffi", path });
await Deno.permissions.query({ name: "run", command: path }); await Deno.permissions.query({ name: "run", command: path });
}); },
);
Deno.test(function permissionURLSync() { Deno.test(
{ permissions: { read: [], write: [], ffi: [], run: [] } },
function permissionURLSync() {
Deno.permissions.querySync({ Deno.permissions.querySync({
name: "read", name: "read",
path: new URL(".", import.meta.url), path: new URL(".", import.meta.url),
@ -156,7 +179,8 @@ Deno.test(function permissionURLSync() {
name: "run", name: "run",
command: new URL(".", import.meta.url), command: new URL(".", import.meta.url),
}); });
}); },
);
Deno.test(async function permissionDescriptorValidation() { Deno.test(async function permissionDescriptorValidation() {
for (const value of [undefined, null, {}]) { for (const value of [undefined, null, {}]) {