summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/args/flags.rs103
-rw-r--r--cli/args/import_map.rs3
-rw-r--r--cli/args/mod.rs7
-rw-r--r--cli/cache/mod.rs8
-rw-r--r--cli/factory.rs53
-rw-r--r--cli/file_fetcher.rs166
-rw-r--r--cli/graph_container.rs4
-rw-r--r--cli/graph_util.rs6
-rw-r--r--cli/jsr.rs11
-rw-r--r--cli/lsp/config.rs24
-rw-r--r--cli/lsp/jsr.rs3
-rw-r--r--cli/lsp/npm.rs3
-rw-r--r--cli/lsp/registries.rs12
-rw-r--r--cli/lsp/testing/execution.rs15
-rw-r--r--cli/module_loader.rs4
-rw-r--r--cli/npm/byonm.rs2
-rw-r--r--cli/npm/managed/resolvers/common.rs3
-rw-r--r--cli/npm/mod.rs6
-rw-r--r--cli/ops/bench.rs19
-rw-r--r--cli/ops/testing.rs19
-rw-r--r--cli/standalone/mod.rs40
-rw-r--r--cli/tools/bench/mod.rs33
-rw-r--r--cli/tools/jupyter/mod.rs4
-rw-r--r--cli/tools/registry/pm/cache_deps.rs2
-rw-r--r--cli/tools/repl/mod.rs10
-rw-r--r--cli/tools/run/mod.rs18
-rw-r--r--cli/tools/serve.rs9
-rw-r--r--cli/tools/test/mod.rs40
-rw-r--r--cli/worker.rs79
-rw-r--r--ext/fetch/lib.rs27
-rw-r--r--ext/ffi/call.rs4
-rw-r--r--ext/ffi/callback.rs2
-rw-r--r--ext/ffi/dlfcn.rs18
-rw-r--r--ext/ffi/lib.rs23
-rw-r--r--ext/ffi/repr.rs42
-rw-r--r--ext/fs/lib.rs80
-rw-r--r--ext/fs/ops.rs302
-rw-r--r--ext/kv/sqlite.rs74
-rw-r--r--ext/napi/lib.rs16
-rw-r--r--ext/net/lib.rs45
-rw-r--r--ext/net/ops.rs21
-rw-r--r--ext/net/ops_tls.rs9
-rw-r--r--ext/net/ops_unix.rs42
-rw-r--r--ext/node/lib.rs35
-rw-r--r--ext/node/ops/fs.rs71
-rw-r--r--runtime/examples/extension/main.rs7
-rw-r--r--runtime/lib.rs1
-rw-r--r--runtime/ops/fs_events.rs5
-rw-r--r--runtime/ops/permissions.rs160
-rw-r--r--runtime/ops/process.rs69
-rw-r--r--runtime/ops/worker_host.rs15
-rw-r--r--runtime/permissions.rs164
-rw-r--r--runtime/permissions/lib.rs2619
-rw-r--r--runtime/snapshot.rs97
-rw-r--r--runtime/web_worker.rs7
-rw-r--r--runtime/worker.rs15
-rw-r--r--tests/integration/run_tests.rs6
-rw-r--r--tests/specs/permission/deny_run_binary_absolute_path/__test__.jsonc8
-rw-r--r--tests/specs/permission/deny_run_binary_absolute_path/main.out8
-rw-r--r--tests/specs/permission/deny_run_binary_absolute_path/main.ts15
-rw-r--r--tests/specs/permission/make_temp_write_perm/056_make_temp_file_write_perm.out4
-rw-r--r--tests/specs/permission/make_temp_write_perm/056_make_temp_file_write_perm.ts52
-rw-r--r--tests/specs/permission/make_temp_write_perm/__test__.jsonc15
-rw-r--r--tests/specs/run/allow_run_allowlist_resolution/__test__.jsonc7
-rw-r--r--tests/specs/run/allow_run_allowlist_resolution/main.out2
-rw-r--r--tests/specs/run/allow_run_allowlist_resolution/main.ts37
-rw-r--r--tests/specs/run/ld_preload/__test__.jsonc4
-rw-r--r--tests/specs/run/ld_preload/set_with_allow_env.ts4
-rw-r--r--tests/testdata/run/056_make_temp_file_write_perm.out1
-rw-r--r--tests/testdata/run/056_make_temp_file_write_perm.ts9
-rw-r--r--tests/testdata/workers/read_check_granular_worker.js18
-rw-r--r--tests/unit/os_test.ts4
-rw-r--r--tests/unit/worker_test.ts39
-rwxr-xr-xtools/lint.js2
74 files changed, 3041 insertions, 1870 deletions
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index f7502b706..f832c2a62 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -32,7 +32,6 @@ use deno_core::normalize_path;
use deno_core::resolve_url_or_path;
use deno_core::url::Url;
use deno_graph::GraphKind;
-use deno_runtime::colors;
use deno_runtime::deno_permissions::parse_sys_kind;
use deno_runtime::deno_permissions::PermissionsOptions;
use log::debug;
@@ -661,107 +660,25 @@ impl PermissionFlags {
|| self.deny_write.is_some()
}
- pub fn to_options(
- &self,
- // will be None when `deno compile` can't resolve the cwd
- initial_cwd: Option<&Path>,
- ) -> Result<PermissionsOptions, AnyError> {
- fn convert_option_str_to_path_buf(
- flag: &Option<Vec<String>>,
- initial_cwd: Option<&Path>,
- ) -> Result<Option<Vec<PathBuf>>, AnyError> {
- let Some(paths) = &flag else {
- return Ok(None);
- };
-
- let mut new_paths = Vec::with_capacity(paths.len());
- for path in paths {
- if let Some(initial_cwd) = initial_cwd {
- new_paths.push(initial_cwd.join(path))
- } else {
- let path = PathBuf::from(path);
- if path.is_absolute() {
- new_paths.push(path);
- } else {
- bail!("Could not resolve relative permission path '{}' when current working directory could not be resolved.", path.display())
- }
- }
- }
- Ok(Some(new_paths))
- }
-
- fn resolve_allow_run(
- allow_run: &[String],
- ) -> Result<Vec<PathBuf>, AnyError> {
- let mut new_allow_run = Vec::with_capacity(allow_run.len());
- for command_name in allow_run {
- if command_name.is_empty() {
- bail!("Empty command name not allowed in --allow-run=...")
- }
- let command_path_result = which::which(command_name);
- match command_path_result {
- Ok(command_path) => new_allow_run.push(command_path),
- Err(err) => {
- log::info!(
- "{} Failed to resolve '{}' for allow-run: {}",
- colors::gray("Info"),
- command_name,
- err
- );
- }
- }
- }
- Ok(new_allow_run)
- }
-
- let mut deny_write =
- convert_option_str_to_path_buf(&self.deny_write, initial_cwd)?;
- let allow_run = self
- .allow_run
- .as_ref()
- .and_then(|raw_allow_run| match resolve_allow_run(raw_allow_run) {
- Ok(resolved_allow_run) => {
- if resolved_allow_run.is_empty() && !raw_allow_run.is_empty() {
- None // convert to no permissions if now empty
- } else {
- Some(Ok(resolved_allow_run))
- }
- }
- Err(err) => Some(Err(err)),
- })
- .transpose()?;
- // add the allow_run list to deno_write
- if let Some(allow_run_vec) = &allow_run {
- if !allow_run_vec.is_empty() {
- let deno_write = deny_write.get_or_insert_with(Vec::new);
- deno_write.extend(allow_run_vec.iter().cloned());
- }
- }
-
- Ok(PermissionsOptions {
+ pub fn to_options(&self) -> PermissionsOptions {
+ PermissionsOptions {
allow_all: self.allow_all,
allow_env: self.allow_env.clone(),
deny_env: self.deny_env.clone(),
allow_net: self.allow_net.clone(),
deny_net: self.deny_net.clone(),
- allow_ffi: convert_option_str_to_path_buf(&self.allow_ffi, initial_cwd)?,
- deny_ffi: convert_option_str_to_path_buf(&self.deny_ffi, initial_cwd)?,
- allow_read: convert_option_str_to_path_buf(
- &self.allow_read,
- initial_cwd,
- )?,
- deny_read: convert_option_str_to_path_buf(&self.deny_read, initial_cwd)?,
- allow_run,
+ allow_ffi: self.allow_ffi.clone(),
+ deny_ffi: self.deny_ffi.clone(),
+ allow_read: self.allow_read.clone(),
+ deny_read: self.deny_read.clone(),
+ allow_run: self.allow_run.clone(),
deny_run: self.deny_run.clone(),
allow_sys: self.allow_sys.clone(),
deny_sys: self.deny_sys.clone(),
- allow_write: convert_option_str_to_path_buf(
- &self.allow_write,
- initial_cwd,
- )?,
- deny_write,
+ allow_write: self.allow_write.clone(),
+ deny_write: self.deny_write.clone(),
prompt: !resolve_no_prompt(self),
- })
+ }
}
}
diff --git a/cli/args/import_map.rs b/cli/args/import_map.rs
index 7a16ab215..ff2f15871 100644
--- a/cli/args/import_map.rs
+++ b/cli/args/import_map.rs
@@ -3,7 +3,6 @@
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::url::Url;
-use deno_runtime::deno_permissions::PermissionsContainer;
use crate::file_fetcher::FileFetcher;
@@ -17,7 +16,7 @@ pub async fn resolve_import_map_value_from_specifier(
Ok(serde_json::from_str(&data_url_text)?)
} else {
let file = file_fetcher
- .fetch(specifier, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(specifier)
.await?
.into_text_decoded()?;
Ok(serde_json::from_str(&file.source)?)
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index 7d885295e..0e4004a53 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -27,7 +27,6 @@ use deno_npm::npm_rc::NpmRc;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmSystemInfo;
-use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference;
use import_map::resolve_import_map_value_from_specifier;
@@ -1082,7 +1081,7 @@ impl CliOptions {
let specifier = specifier.clone();
async move {
let file = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(&specifier)
.await?
.into_text_decoded()?;
Ok(file.source.to_string())
@@ -1501,8 +1500,8 @@ impl CliOptions {
&self.flags.permissions
}
- pub fn permissions_options(&self) -> Result<PermissionsOptions, AnyError> {
- self.flags.permissions.to_options(Some(&self.initial_cwd))
+ pub fn permissions_options(&self) -> PermissionsOptions {
+ self.flags.permissions.to_options()
}
pub fn reload_flag(&self) -> bool {
diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs
index 3b4e27760..86b65bc53 100644
--- a/cli/cache/mod.rs
+++ b/cli/cache/mod.rs
@@ -4,6 +4,7 @@ use crate::args::CacheSetting;
use crate::errors::get_error_class_name;
use crate::file_fetcher::FetchNoFollowOptions;
use crate::file_fetcher::FetchOptions;
+use crate::file_fetcher::FetchPermissionsOption;
use crate::file_fetcher::FileFetcher;
use crate::file_fetcher::FileOrRedirect;
use crate::npm::CliNpmResolver;
@@ -18,7 +19,6 @@ use deno_graph::source::CacheInfo;
use deno_graph::source::LoadFuture;
use deno_graph::source::LoadResponse;
use deno_graph::source::Loader;
-use deno_runtime::deno_permissions::PermissionsContainer;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
@@ -112,7 +112,7 @@ pub struct FetchCacher {
global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
- permissions: PermissionsContainer,
+ permissions: FetchPermissionsOption,
cache_info_enabled: bool,
}
@@ -123,7 +123,7 @@ impl FetchCacher {
global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
- permissions: PermissionsContainer,
+ permissions: FetchPermissionsOption,
) -> Self {
Self {
file_fetcher,
@@ -230,7 +230,7 @@ impl Loader for FetchCacher {
.fetch_no_follow_with_options(FetchNoFollowOptions {
fetch_options: FetchOptions {
specifier: &specifier,
- permissions: &permissions,
+ permissions: permissions.as_ref(),
maybe_accept: None,
maybe_cache_setting: maybe_cache_setting.as_ref(),
},
diff --git a/cli/factory.rs b/cli/factory.rs
index 1ec2104ed..fe6d5b92a 100644
--- a/cli/factory.rs
+++ b/cli/factory.rs
@@ -65,10 +65,13 @@ use deno_core::FeatureChecker;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_runtime::deno_node::NodeResolver;
+use deno_runtime::deno_permissions::Permissions;
+use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore;
use deno_runtime::inspector_server::InspectorServer;
+use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use log::warn;
use node_resolver::analyze::NodeCodeTranslator;
use once_cell::sync::OnceCell;
@@ -181,6 +184,7 @@ struct CliFactoryServices {
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
node_resolver: Deferred<Arc<NodeResolver>>,
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
+ permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
sloppy_imports_resolver: Deferred<Option<Arc<SloppyImportsResolver>>>,
text_only_progress_bar: Deferred<ProgressBar>,
type_checker: Deferred<Arc<TypeChecker>>,
@@ -708,6 +712,15 @@ impl CliFactory {
.await
}
+ pub fn permission_desc_parser(
+ &self,
+ ) -> Result<&Arc<RuntimePermissionDescriptorParser>, AnyError> {
+ self.services.permission_desc_parser.get_or_try_init(|| {
+ let fs = self.fs().clone();
+ Ok(Arc::new(RuntimePermissionDescriptorParser::new(fs)))
+ })
+ }
+
pub fn feature_checker(&self) -> Result<&Arc<FeatureChecker>, AnyError> {
self.services.feature_checker.get_or_try_init(|| {
let cli_options = self.cli_options()?;
@@ -739,6 +752,17 @@ impl CliFactory {
))
}
+ pub fn create_permissions_container(
+ &self,
+ ) -> Result<PermissionsContainer, AnyError> {
+ let desc_parser = self.permission_desc_parser()?.clone();
+ let permissions = Permissions::from_options(
+ desc_parser.as_ref(),
+ &self.cli_options()?.permissions_options(),
+ )?;
+ Ok(PermissionsContainer::new(desc_parser, permissions))
+ }
+
pub async fn create_cli_main_worker_factory(
&self,
) -> Result<CliMainWorkerFactory, AnyError> {
@@ -754,11 +778,17 @@ impl CliFactory {
};
Ok(CliMainWorkerFactory::new(
- StorageKeyResolver::from_options(cli_options),
- cli_options.sub_command().clone(),
- npm_resolver.clone(),
- node_resolver.clone(),
self.blob_store().clone(),
+ if cli_options.code_cache_enabled() {
+ Some(self.code_cache()?.clone())
+ } else {
+ None
+ },
+ self.feature_checker()?.clone(),
+ self.fs().clone(),
+ maybe_file_watcher_communicator,
+ self.maybe_inspector_server()?.clone(),
+ cli_options.maybe_lockfile().cloned(),
Box::new(CliModuleLoaderFactory::new(
cli_options,
if cli_options.code_cache_enabled() {
@@ -779,17 +809,12 @@ impl CliFactory {
self.parsed_source_cache().clone(),
self.resolver().await?.clone(),
)),
+ node_resolver.clone(),
+ npm_resolver.clone(),
+ self.permission_desc_parser()?.clone(),
self.root_cert_store_provider().clone(),
- self.fs().clone(),
- maybe_file_watcher_communicator,
- self.maybe_inspector_server()?.clone(),
- cli_options.maybe_lockfile().cloned(),
- self.feature_checker()?.clone(),
- if cli_options.code_cache_enabled() {
- Some(self.code_cache()?.clone())
- } else {
- None
- },
+ StorageKeyResolver::from_options(cli_options),
+ cli_options.sub_command().clone(),
self.create_cli_main_worker_options()?,
))
}
diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs
index ace4d3c7e..ca8fcbee4 100644
--- a/cli/file_fetcher.rs
+++ b/cli/file_fetcher.rs
@@ -161,9 +161,38 @@ fn get_validated_scheme(
}
}
+#[derive(Debug, Copy, Clone)]
+pub enum FetchPermissionsOptionRef<'a> {
+ AllowAll,
+ Container(&'a PermissionsContainer),
+}
+
+#[derive(Debug, Clone)]
+pub enum FetchPermissionsOption {
+ AllowAll,
+ Container(PermissionsContainer),
+}
+
+impl FetchPermissionsOption {
+ pub fn as_ref(&self) -> FetchPermissionsOptionRef {
+ match self {
+ FetchPermissionsOption::AllowAll => FetchPermissionsOptionRef::AllowAll,
+ FetchPermissionsOption::Container(container) => {
+ FetchPermissionsOptionRef::Container(container)
+ }
+ }
+ }
+}
+
+impl From<PermissionsContainer> for FetchPermissionsOption {
+ fn from(value: PermissionsContainer) -> Self {
+ Self::Container(value)
+ }
+}
+
pub struct FetchOptions<'a> {
pub specifier: &'a ModuleSpecifier,
- pub permissions: &'a PermissionsContainer,
+ pub permissions: FetchPermissionsOptionRef<'a>,
pub maybe_accept: Option<&'a str>,
pub maybe_cache_setting: Option<&'a CacheSetting>,
}
@@ -515,13 +544,35 @@ impl FileFetcher {
}
}
+ #[inline(always)]
+ pub async fn fetch_bypass_permissions(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> Result<File, AnyError> {
+ self
+ .fetch_inner(specifier, FetchPermissionsOptionRef::AllowAll)
+ .await
+ }
+
/// Fetch a source file and asynchronously return it.
+ #[allow(dead_code)] // todo(25469): undo when merging
+ #[inline(always)]
pub async fn fetch(
&self,
specifier: &ModuleSpecifier,
permissions: &PermissionsContainer,
) -> Result<File, AnyError> {
self
+ .fetch_inner(specifier, FetchPermissionsOptionRef::Container(permissions))
+ .await
+ }
+
+ async fn fetch_inner(
+ &self,
+ specifier: &ModuleSpecifier,
+ permissions: FetchPermissionsOptionRef<'_>,
+ ) -> Result<File, AnyError> {
+ self
.fetch_with_options(FetchOptions {
specifier,
permissions,
@@ -583,7 +634,14 @@ impl FileFetcher {
specifier
);
let scheme = get_validated_scheme(specifier)?;
- options.permissions.check_specifier(specifier)?;
+ match options.permissions {
+ FetchPermissionsOptionRef::AllowAll => {
+ // allow
+ }
+ FetchPermissionsOptionRef::Container(permissions) => {
+ permissions.check_specifier(specifier)?;
+ }
+ }
if let Some(file) = self.memory_files.get(specifier) {
Ok(FileOrRedirect::File(file))
} else if scheme == "file" {
@@ -684,9 +742,7 @@ mod tests {
async fn test_fetch(specifier: &ModuleSpecifier) -> (File, FileFetcher) {
let (file_fetcher, _) = setup(CacheSetting::ReloadAll, None);
- let result = file_fetcher
- .fetch(specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(specifier).await;
assert!(result.is_ok());
(result.unwrap(), file_fetcher)
}
@@ -700,7 +756,7 @@ mod tests {
.fetch_with_options_and_max_redirect(
FetchOptions {
specifier,
- permissions: &PermissionsContainer::allow_all(),
+ permissions: FetchPermissionsOptionRef::AllowAll,
maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting),
},
@@ -796,9 +852,7 @@ mod tests {
};
file_fetcher.insert_memory_files(file.clone());
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let result_file = result.unwrap();
assert_eq!(result_file, file);
@@ -809,9 +863,7 @@ mod tests {
let (file_fetcher, _) = setup(CacheSetting::Use, None);
let specifier = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(
@@ -840,9 +892,7 @@ mod tests {
None,
);
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(
@@ -862,9 +912,7 @@ mod tests {
let specifier =
ModuleSpecifier::parse("http://localhost:4545/subdir/mod2.ts").unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(
@@ -882,9 +930,7 @@ mod tests {
.set(&specifier, headers.clone(), file.source.as_bytes())
.unwrap();
- let result = file_fetcher_01
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(
@@ -908,9 +954,7 @@ mod tests {
.set(&specifier, headers.clone(), file.source.as_bytes())
.unwrap();
- let result = file_fetcher_02
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(
@@ -933,9 +977,7 @@ mod tests {
Default::default(),
None,
);
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(
@@ -966,9 +1008,7 @@ mod tests {
None,
);
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let cache_key =
file_fetcher.http_cache.cache_item_key(&specifier).unwrap();
@@ -1002,9 +1042,7 @@ mod tests {
Default::default(),
None,
);
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let cache_key =
@@ -1041,9 +1079,7 @@ mod tests {
resolve_url("http://localhost:4545/subdir/redirects/redirect1.js")
.unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap();
assert_eq!(file.specifier, redirected_specifier);
@@ -1082,9 +1118,7 @@ mod tests {
resolve_url("http://localhost:4545/subdir/redirects/redirect1.js")
.unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap();
assert_eq!(file.specifier, redirected_02_specifier);
@@ -1142,9 +1176,7 @@ mod tests {
None,
);
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let cache_key = file_fetcher
@@ -1182,7 +1214,7 @@ mod tests {
None,
);
let result = file_fetcher
- .fetch(&redirected_specifier, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(&redirected_specifier)
.await;
assert!(result.is_ok());
@@ -1223,7 +1255,7 @@ mod tests {
.fetch_with_options_and_max_redirect(
FetchOptions {
specifier: &specifier,
- permissions: &PermissionsContainer::allow_all(),
+ permissions: FetchPermissionsOptionRef::AllowAll,
maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting),
},
@@ -1236,7 +1268,7 @@ mod tests {
.fetch_with_options_and_max_redirect(
FetchOptions {
specifier: &specifier,
- permissions: &PermissionsContainer::allow_all(),
+ permissions: FetchPermissionsOptionRef::AllowAll,
maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting),
},
@@ -1264,9 +1296,7 @@ mod tests {
resolve_url("http://localhost:4550/subdir/redirects/redirect1.js")
.unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap();
assert_eq!(file.specifier, redirected_specifier);
@@ -1310,9 +1340,7 @@ mod tests {
let specifier =
resolve_url("http://localhost:4545/run/002_hello.ts").unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(get_custom_error_class(&err), Some("NoRemote"));
@@ -1343,22 +1371,16 @@ mod tests {
let specifier =
resolve_url("http://localhost:4545/run/002_hello.ts").unwrap();
- let result = file_fetcher_01
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.to_string(), "Specifier not found in cache: \"http://localhost:4545/run/002_hello.ts\", --cached-only is specified.");
assert_eq!(get_custom_error_class(&err), Some("NotCached"));
- let result = file_fetcher_02
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
- let result = file_fetcher_01
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
}
@@ -1368,17 +1390,13 @@ mod tests {
let fixture_path = temp_dir.path().join("mod.ts");
let specifier = ModuleSpecifier::from_file_path(&fixture_path).unwrap();
fs::write(fixture_path.clone(), r#"console.log("hello deno");"#).unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(&*file.source, r#"console.log("hello deno");"#);
fs::write(fixture_path, r#"console.log("goodbye deno");"#).unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(&*file.source, r#"console.log("goodbye deno");"#);
@@ -1392,18 +1410,14 @@ mod tests {
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
let specifier =
ModuleSpecifier::parse("http://localhost:4545/dynamic").unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap();
let first = file.source;
let (file_fetcher, _) =
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap();
let second = file.source;
@@ -1419,18 +1433,14 @@ mod tests {
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
let specifier =
ModuleSpecifier::parse("http://localhost:4545/dynamic_cache").unwrap();
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap();
let first = file.source;
let (file_fetcher, _) =
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
- let result = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await;
+ let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
assert!(result.is_ok());
let file = result.unwrap();
let second = file.source;
diff --git a/cli/graph_container.rs b/cli/graph_container.rs
index 9f71045c6..9f049946f 100644
--- a/cli/graph_container.rs
+++ b/cli/graph_container.rs
@@ -9,9 +9,9 @@ use deno_core::error::AnyError;
use deno_core::parking_lot::RwLock;
use deno_graph::ModuleGraph;
use deno_runtime::colors;
-use deno_runtime::deno_permissions::PermissionsContainer;
use crate::args::CliOptions;
+use crate::file_fetcher::FetchPermissionsOption;
use crate::module_loader::ModuleLoadPreparer;
use crate::util::fs::collect_specifiers;
use crate::util::path::is_script_ext;
@@ -75,7 +75,7 @@ impl MainModuleGraphContainer {
specifiers,
false,
self.cli_options.ts_type_lib_window(),
- PermissionsContainer::allow_all(),
+ FetchPermissionsOption::AllowAll,
)
.await?;
graph_permit.commit();
diff --git a/cli/graph_util.rs b/cli/graph_util.rs
index d73733123..cb2e1bde8 100644
--- a/cli/graph_util.rs
+++ b/cli/graph_util.rs
@@ -11,6 +11,7 @@ use crate::cache::ModuleInfoCache;
use crate::cache::ParsedSourceCache;
use crate::colors;
use crate::errors::get_error_class_name;
+use crate::file_fetcher::FetchPermissionsOption;
use crate::file_fetcher::FileFetcher;
use crate::npm::CliNpmResolver;
use crate::resolver::CliGraphResolver;
@@ -41,7 +42,6 @@ use deno_graph::ResolutionError;
use deno_graph::SpecifierError;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node;
-use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::jsr::JsrDepPackageReq;
use deno_semver::package::PackageNv;
use deno_semver::Version;
@@ -670,12 +670,12 @@ impl ModuleGraphBuilder {
/// Creates the default loader used for creating a graph.
pub fn create_graph_loader(&self) -> cache::FetchCacher {
- self.create_fetch_cacher(PermissionsContainer::allow_all())
+ self.create_fetch_cacher(FetchPermissionsOption::AllowAll)
}
pub fn create_fetch_cacher(
&self,
- permissions: PermissionsContainer,
+ permissions: FetchPermissionsOption,
) -> cache::FetchCacher {
cache::FetchCacher::new(
self.file_fetcher.clone(),
diff --git a/cli/jsr.rs b/cli/jsr.rs
index 87a54af22..767d304d6 100644
--- a/cli/jsr.rs
+++ b/cli/jsr.rs
@@ -6,7 +6,6 @@ use dashmap::DashMap;
use deno_core::serde_json;
use deno_graph::packages::JsrPackageInfo;
use deno_graph::packages::JsrPackageVersionInfo;
-use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use std::sync::Arc;
@@ -68,10 +67,7 @@ impl JsrFetchResolver {
let file_fetcher = self.file_fetcher.clone();
// spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move {
- file_fetcher
- .fetch(&meta_url, &PermissionsContainer::allow_all())
- .await
- .ok()
+ file_fetcher.fetch_bypass_permissions(&meta_url).await.ok()
})
.await
.ok()??;
@@ -96,10 +92,7 @@ impl JsrFetchResolver {
let file_fetcher = self.file_fetcher.clone();
// spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move {
- file_fetcher
- .fetch(&meta_url, &PermissionsContainer::allow_all())
- .await
- .ok()
+ file_fetcher.fetch_bypass_permissions(&meta_url).await.ok()
})
.await
.ok()??;
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index c5d064285..f69cae435 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -37,7 +37,6 @@ use deno_lint::linter::LintConfig as DenoLintConfig;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonCache;
use deno_runtime::deno_node::PackageJson;
-use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::fs_util::specifier_to_file_path;
use indexmap::IndexSet;
use lsp_types::ClientCapabilities;
@@ -1509,17 +1508,16 @@ impl ConfigData {
ConfigWatchedFileType::ImportMap,
);
// spawn due to the lsp's `Send` requirement
- let fetch_result = deno_core::unsync::spawn({
- let file_fetcher = file_fetcher.cloned().unwrap();
- let import_map_url = import_map_url.clone();
- async move {
- file_fetcher
- .fetch(&import_map_url, &PermissionsContainer::allow_all())
- .await
- }
- })
- .await
- .unwrap();
+ let fetch_result =
+ deno_core::unsync::spawn({
+ let file_fetcher = file_fetcher.cloned().unwrap();
+ let import_map_url = import_map_url.clone();
+ async move {
+ file_fetcher.fetch_bypass_permissions(&import_map_url).await
+ }
+ })
+ .await
+ .unwrap();
let value_result = fetch_result.and_then(|f| {
serde_json::from_slice::<Value>(&f.source).map_err(|e| e.into())
@@ -1558,7 +1556,7 @@ impl ConfigData {
let file_fetcher = file_fetcher.clone().unwrap();
async move {
let file = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(&specifier)
.await?
.into_text_decoded()?;
Ok(file.source.to_string())
diff --git a/cli/lsp/jsr.rs b/cli/lsp/jsr.rs
index 6c591637c..9a738ec28 100644
--- a/cli/lsp/jsr.rs
+++ b/cli/lsp/jsr.rs
@@ -14,7 +14,6 @@ use deno_graph::packages::JsrPackageInfo;
use deno_graph::packages::JsrPackageInfoVersion;
use deno_graph::packages::JsrPackageVersionInfo;
use deno_graph::ModuleSpecifier;
-use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
@@ -311,7 +310,7 @@ impl PackageSearchApi for CliJsrSearchApi {
// spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move {
file_fetcher
- .fetch(&search_url, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(&search_url)
.await?
.into_text_decoded()
})
diff --git a/cli/lsp/npm.rs b/cli/lsp/npm.rs
index e5aa33744..8bdeb7e7d 100644
--- a/cli/lsp/npm.rs
+++ b/cli/lsp/npm.rs
@@ -4,7 +4,6 @@ use dashmap::DashMap;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::serde_json;
-use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::package::PackageNv;
use deno_semver::Version;
use serde::Deserialize;
@@ -55,7 +54,7 @@ impl PackageSearchApi for CliNpmSearchApi {
let file_fetcher = self.file_fetcher.clone();
let file = deno_core::unsync::spawn(async move {
file_fetcher
- .fetch(&search_url, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(&search_url)
.await?
.into_text_decoded()
})
diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs
index c709831d4..5f7ce0082 100644
--- a/cli/lsp/registries.rs
+++ b/cli/lsp/registries.rs
@@ -16,6 +16,7 @@ use crate::args::CacheSetting;
use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache;
use crate::file_fetcher::FetchOptions;
+use crate::file_fetcher::FetchPermissionsOptionRef;
use crate::file_fetcher::FileFetcher;
use crate::http_util::HttpClientProvider;
@@ -30,7 +31,6 @@ use deno_core::url::Position;
use deno_core::url::Url;
use deno_core::ModuleSpecifier;
use deno_graph::Dependency;
-use deno_runtime::deno_permissions::PermissionsContainer;
use log::error;
use once_cell::sync::Lazy;
use std::borrow::Cow;
@@ -481,7 +481,7 @@ impl ModuleRegistry {
file_fetcher
.fetch_with_options(FetchOptions {
specifier: &specifier,
- permissions: &PermissionsContainer::allow_all(),
+ permissions: FetchPermissionsOptionRef::AllowAll,
maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"),
maybe_cache_setting: None,
})
@@ -584,7 +584,7 @@ impl ModuleRegistry {
let file = deno_core::unsync::spawn({
async move {
file_fetcher
- .fetch(&endpoint, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(&endpoint)
.await
.ok()?
.into_text_decoded()
@@ -983,7 +983,7 @@ impl ModuleRegistry {
// spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move {
file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(&specifier)
.await
.ok()?
.into_text_decoded()
@@ -1049,7 +1049,7 @@ impl ModuleRegistry {
let specifier = specifier.clone();
async move {
file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(&specifier)
.await
.map_err(|err| {
error!(
@@ -1095,7 +1095,7 @@ impl ModuleRegistry {
let specifier = specifier.clone();
async move {
file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
+ .fetch_bypass_permissions(&specifier)
.await
.map_err(|err| {
error!(
diff --git a/cli/lsp/testing/execution.rs b/cli/lsp/testing/execution.rs
index c2398d4ff..a8cea8dd4 100644
--- a/cli/lsp/testing/execution.rs
+++ b/cli/lsp/testing/execution.rs
@@ -31,6 +31,7 @@ use deno_core::unsync::spawn;
use deno_core::unsync::spawn_blocking;
use deno_core::ModuleSpecifier;
use deno_runtime::deno_permissions::Permissions;
+use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::tokio_util::create_and_run_current_thread;
use indexmap::IndexMap;
use std::borrow::Cow;
@@ -227,8 +228,11 @@ impl TestRun {
// Various test files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
- let permissions =
- Permissions::from_options(&cli_options.permissions_options()?)?;
+ let permission_desc_parser = factory.permission_desc_parser()?.clone();
+ let permissions = Permissions::from_options(
+ permission_desc_parser.as_ref(),
+ &cli_options.permissions_options(),
+ )?;
let main_graph_container = factory.main_module_graph_container().await?;
test::check_specifiers(
factory.file_fetcher()?,
@@ -276,7 +280,10 @@ impl TestRun {
let join_handles = queue.into_iter().map(move |specifier| {
let specifier = specifier.clone();
let worker_factory = worker_factory.clone();
- let permissions = permissions.clone();
+ let permissions_container = PermissionsContainer::new(
+ permission_desc_parser.clone(),
+ permissions.clone(),
+ );
let worker_sender = test_event_sender_factory.worker();
let fail_fast_tracker = fail_fast_tracker.clone();
let lsp_filter = self.filters.get(&specifier);
@@ -305,7 +312,7 @@ impl TestRun {
// channel.
create_and_run_current_thread(test::test_specifier(
worker_factory,
- permissions,
+ permissions_container,
specifier,
worker_sender,
fail_fast_tracker,
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index a35e60090..86824cd70 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -104,7 +104,7 @@ impl ModuleLoadPreparer {
roots: &[ModuleSpecifier],
is_dynamic: bool,
lib: TsTypeLib,
- permissions: PermissionsContainer,
+ permissions: crate::file_fetcher::FetchPermissionsOption,
) -> Result<(), AnyError> {
log::debug!("Preparing module load.");
let _pb_clear_guard = self.progress_bar.clear_guard();
@@ -762,7 +762,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
&[specifier],
is_dynamic,
lib,
- root_permissions,
+ root_permissions.into(),
)
.await?;
update_permit.commit();
diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs
index 3249b2ed1..24645e416 100644
--- a/cli/npm/byonm.rs
+++ b/cli/npm/byonm.rs
@@ -280,7 +280,7 @@ impl NodeRequireResolver for ByonmCliNpmResolver {
.components()
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
{
- permissions.check_read(path)?;
+ _ = permissions.check_read_path(path)?;
}
Ok(())
}
diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs
index 170dc2ae6..1893aa56a 100644
--- a/cli/npm/managed/resolvers/common.rs
+++ b/cli/npm/managed/resolvers/common.rs
@@ -127,7 +127,8 @@ impl RegistryReadPermissionChecker {
}
}
- permissions.check_read(path)
+ _ = permissions.check_read_path(path)?;
+ Ok(())
}
}
diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs
index c8f87e6af..bedde6455 100644
--- a/cli/npm/mod.rs
+++ b/cli/npm/mod.rs
@@ -15,7 +15,6 @@ use deno_core::serde_json;
use deno_npm::registry::NpmPackageInfo;
use deno_runtime::deno_node::NodeRequireResolver;
use deno_runtime::deno_node::NpmProcessStateProvider;
-use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use node_resolver::NpmResolver;
@@ -152,10 +151,7 @@ impl NpmFetchResolver {
let file_fetcher = self.file_fetcher.clone();
// spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move {
- file_fetcher
- .fetch(&info_url, &PermissionsContainer::allow_all())
- .await
- .ok()
+ file_fetcher.fetch_bypass_permissions(&info_url).await.ok()
})
.await
.ok()??;
diff --git a/cli/ops/bench.rs b/cli/ops/bench.rs
index 5521253ff..edd8c118c 100644
--- a/cli/ops/bench.rs
+++ b/cli/ops/bench.rs
@@ -2,6 +2,7 @@
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
+use std::sync::Arc;
use std::time;
use deno_core::error::generic_error;
@@ -13,6 +14,7 @@ use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_runtime::deno_permissions::create_child_permissions;
use deno_runtime::deno_permissions::ChildPermissionsArg;
+use deno_runtime::deno_permissions::PermissionDescriptorParser;
use deno_runtime::deno_permissions::PermissionsContainer;
use tokio::sync::mpsc::UnboundedSender;
use uuid::Uuid;
@@ -59,11 +61,18 @@ pub fn op_pledge_test_permissions(
#[serde] args: ChildPermissionsArg,
) -> Result<Uuid, AnyError> {
let token = Uuid::new_v4();
+ let permission_desc_parser = state
+ .borrow::<Arc<dyn PermissionDescriptorParser>>()
+ .clone();
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = {
- let mut parent_permissions = parent_permissions.0.lock();
- let perms = create_child_permissions(&mut parent_permissions, args)?;
- PermissionsContainer::new(perms)
+ let mut parent_permissions = parent_permissions.inner.lock();
+ let perms = create_child_permissions(
+ permission_desc_parser.as_ref(),
+ &mut parent_permissions,
+ args,
+ )?;
+ PermissionsContainer::new(permission_desc_parser, perms)
};
let parent_permissions = parent_permissions.clone();
@@ -74,7 +83,7 @@ pub fn op_pledge_test_permissions(
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
// NOTE: This call overrides current permission set for the worker
- state.put(worker_permissions.0.clone());
+ state.put(worker_permissions.inner.clone());
state.put::<PermissionsContainer>(worker_permissions);
Ok(token)
@@ -91,7 +100,7 @@ pub fn op_restore_test_permissions(
}
let permissions = permissions_holder.1;
- state.put(permissions.0.clone());
+ state.put(permissions.inner.clone());
state.put::<PermissionsContainer>(permissions);
Ok(())
} else {
diff --git a/cli/ops/testing.rs b/cli/ops/testing.rs
index b8839a6f8..6a8d31006 100644
--- a/cli/ops/testing.rs
+++ b/cli/ops/testing.rs
@@ -18,9 +18,11 @@ use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_runtime::deno_permissions::create_child_permissions;
use deno_runtime::deno_permissions::ChildPermissionsArg;
+use deno_runtime::deno_permissions::PermissionDescriptorParser;
use deno_runtime::deno_permissions::PermissionsContainer;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
+use std::sync::Arc;
use uuid::Uuid;
deno_core::extension!(deno_test,
@@ -54,11 +56,18 @@ pub fn op_pledge_test_permissions(
#[serde] args: ChildPermissionsArg,
) -> Result<Uuid, AnyError> {
let token = Uuid::new_v4();
+ let permission_desc_parser = state
+ .borrow::<Arc<dyn PermissionDescriptorParser>>()
+ .clone();
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = {
- let mut parent_permissions = parent_permissions.0.lock();
- let perms = create_child_permissions(&mut parent_permissions, args)?;
- PermissionsContainer::new(perms)
+ let mut parent_permissions = parent_permissions.inner.lock();
+ let perms = create_child_permissions(
+ permission_desc_parser.as_ref(),
+ &mut parent_permissions,
+ args,
+ )?;
+ PermissionsContainer::new(permission_desc_parser, perms)
};
let parent_permissions = parent_permissions.clone();
@@ -68,7 +77,7 @@ pub fn op_pledge_test_permissions(
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
// NOTE: This call overrides current permission set for the worker
- state.put(worker_permissions.0.clone());
+ state.put(worker_permissions.inner.clone());
state.put::<PermissionsContainer>(worker_permissions);
Ok(token)
@@ -85,7 +94,7 @@ pub fn op_restore_test_permissions(
}
let permissions = permissions_holder.1;
- state.put(permissions.0.clone());
+ state.put(permissions.inner.clone());
state.put::<PermissionsContainer>(permissions);
Ok(())
} else {
diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs
index 0a08296d9..40968a8c4 100644
--- a/cli/standalone/mod.rs
+++ b/cli/standalone/mod.rs
@@ -32,6 +32,8 @@ use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider;
+use deno_runtime::deno_web::BlobStore;
+use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::WorkerExecutionMode;
use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference;
@@ -449,7 +451,6 @@ pub async fn run(
let current_exe_path = std::env::current_exe().unwrap();
let current_exe_name =
current_exe_path.file_name().unwrap().to_string_lossy();
- let maybe_cwd = std::env::current_dir().ok();
let deno_dir_provider = Arc::new(DenoDirProvider::new(None));
let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider {
ca_stores: metadata.ca_stores,
@@ -660,8 +661,7 @@ pub async fn run(
};
let permissions = {
- let mut permissions =
- metadata.permissions.to_options(maybe_cwd.as_deref())?;
+ let mut permissions = metadata.permissions.to_options();
// if running with an npm vfs, grant read access to it
if let Some(vfs_root) = maybe_vfs_root {
match &mut permissions.allow_read {
@@ -669,15 +669,20 @@ pub async fn run(
// do nothing, already granted
}
Some(vec) => {
- vec.push(vfs_root);
+ vec.push(vfs_root.to_string_lossy().to_string());
}
None => {
- permissions.allow_read = Some(vec![vfs_root]);
+ permissions.allow_read =
+ Some(vec![vfs_root.to_string_lossy().to_string()]);
}
}
}
- PermissionsContainer::new(Permissions::from_options(&permissions)?)
+ let desc_parser =
+ Arc::new(RuntimePermissionDescriptorParser::new(fs.clone()));
+ let permissions =
+ Permissions::from_options(desc_parser.as_ref(), &permissions)?;
+ PermissionsContainer::new(desc_parser, permissions)
};
let feature_checker = Arc::new({
let mut checker = FeatureChecker::default();
@@ -689,21 +694,24 @@ pub async fn run(
}
checker
});
+ let permission_desc_parser =
+ Arc::new(RuntimePermissionDescriptorParser::new(fs.clone()));
let worker_factory = CliMainWorkerFactory::new(
- StorageKeyResolver::empty(),
- crate::args::DenoSubcommand::Run(Default::default()),
- npm_resolver,
- node_resolver,
- Default::default(),
- Box::new(module_loader_factory),
- root_cert_store_provider,
- fs,
+ Arc::new(BlobStore::default()),
+ // Code cache is not supported for standalone binary yet.
None,
+ feature_checker,
+ fs,
None,
None,
- feature_checker,
- // Code cache is not supported for standalone binary yet.
None,
+ Box::new(module_loader_factory),
+ node_resolver,
+ npm_resolver,
+ permission_desc_parser,
+ root_cert_store_provider,
+ StorageKeyResolver::empty(),
+ crate::args::DenoSubcommand::Run(Default::default()),
CliMainWorkerOptions {
argv: metadata.argv,
log_level: WorkerLogLevel::Info,
diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs
index 44ae8321d..f133759c9 100644
--- a/cli/tools/bench/mod.rs
+++ b/cli/tools/bench/mod.rs
@@ -30,6 +30,7 @@ use deno_core::ModuleSpecifier;
use deno_core::PollEventLoopOptions;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
+use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::tokio_util::create_and_run_current_thread;
use deno_runtime::WorkerExecutionMode;
use indexmap::IndexMap;
@@ -144,14 +145,14 @@ fn create_reporter(
/// Run a single specifier as an executable bench module.
async fn bench_specifier(
worker_factory: Arc<CliMainWorkerFactory>,
- permissions: Permissions,
+ permissions_container: PermissionsContainer,
specifier: ModuleSpecifier,
sender: UnboundedSender<BenchEvent>,
filter: TestFilter,
) -> Result<(), AnyError> {
match bench_specifier_inner(
worker_factory,
- permissions,
+ permissions_container,
specifier.clone(),
&sender,
filter,
@@ -176,7 +177,7 @@ async fn bench_specifier(
/// Run a single specifier as an executable bench module.
async fn bench_specifier_inner(
worker_factory: Arc<CliMainWorkerFactory>,
- permissions: Permissions,
+ permissions_container: PermissionsContainer,
specifier: ModuleSpecifier,
sender: &UnboundedSender<BenchEvent>,
filter: TestFilter,
@@ -185,7 +186,7 @@ async fn bench_specifier_inner(
.create_custom_worker(
WorkerExecutionMode::Bench,
specifier.clone(),
- PermissionsContainer::new(permissions),
+ permissions_container,
vec![ops::bench::deno_bench::init_ops(sender.clone())],
Default::default(),
)
@@ -264,6 +265,7 @@ async fn bench_specifier_inner(
async fn bench_specifiers(
worker_factory: Arc<CliMainWorkerFactory>,
permissions: &Permissions,
+ permissions_desc_parser: &Arc<RuntimePermissionDescriptorParser>,
specifiers: Vec<ModuleSpecifier>,
options: BenchSpecifierOptions,
) -> Result<(), AnyError> {
@@ -273,13 +275,16 @@ async fn bench_specifiers(
let join_handles = specifiers.into_iter().map(move |specifier| {
let worker_factory = worker_factory.clone();
- let permissions = permissions.clone();
+ let permissions_container = PermissionsContainer::new(
+ permissions_desc_parser.clone(),
+ permissions.clone(),
+ );
let sender = sender.clone();
let options = option_for_handles.clone();
spawn_blocking(move || {
let future = bench_specifier(
worker_factory,
- permissions,
+ permissions_container,
specifier,
sender,
options.filter,
@@ -410,8 +415,11 @@ pub async fn run_benchmarks(
// Various bench files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
- let permissions =
- Permissions::from_options(&cli_options.permissions_options()?)?;
+ let permission_desc_parser = factory.permission_desc_parser()?.clone();
+ let permissions = Permissions::from_options(
+ permission_desc_parser.as_ref(),
+ &cli_options.permissions_options(),
+ )?;
let members_with_bench_options =
cli_options.resolve_bench_options_for_members(&bench_flags)?;
@@ -446,6 +454,7 @@ pub async fn run_benchmarks(
bench_specifiers(
worker_factory,
&permissions,
+ &permission_desc_parser,
specifiers,
BenchSpecifierOptions {
filter: TestFilter::from_flag(&workspace_bench_options.filter),
@@ -519,8 +528,11 @@ pub async fn run_benchmarks_with_watch(
// Various bench files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
- let permissions =
- Permissions::from_options(&cli_options.permissions_options()?)?;
+ let permission_desc_parser = factory.permission_desc_parser()?.clone();
+ let permissions = Permissions::from_options(
+ permission_desc_parser.as_ref(),
+ &cli_options.permissions_options(),
+ )?;
let graph = module_graph_creator
.create_graph(graph_kind, collected_bench_modules.clone())
@@ -568,6 +580,7 @@ pub async fn run_benchmarks_with_watch(
bench_specifiers(
worker_factory,
&permissions,
+ &permission_desc_parser,
specifiers,
BenchSpecifierOptions {
filter: TestFilter::from_flag(&workspace_bench_options.filter),
diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs
index 14fcbd72c..71e947ddd 100644
--- a/cli/tools/jupyter/mod.rs
+++ b/cli/tools/jupyter/mod.rs
@@ -25,7 +25,6 @@ use deno_core::serde_json::json;
use deno_core::url::Url;
use deno_runtime::deno_io::Stdio;
use deno_runtime::deno_io::StdioPipe;
-use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::WorkerExecutionMode;
use deno_terminal::colors;
@@ -65,7 +64,8 @@ pub async fn kernel(
resolve_url_or_path("./$deno$jupyter.ts", cli_options.initial_cwd())
.unwrap();
// TODO(bartlomieju): should we run with all permissions?
- let permissions = PermissionsContainer::new(Permissions::allow_all());
+ let permissions =
+ PermissionsContainer::allow_all(factory.permission_desc_parser()?.clone());
let npm_resolver = factory.npm_resolver().await?.clone();
let resolver = factory.resolver().await?.clone();
let worker_factory = factory.create_cli_main_worker_factory().await?;
diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs
index d292c32f5..a03c30df8 100644
--- a/cli/tools/registry/pm/cache_deps.rs
+++ b/cli/tools/registry/pm/cache_deps.rs
@@ -106,7 +106,7 @@ pub async fn cache_top_level_deps(
&roots,
false,
deno_config::deno_json::TsTypeLib::DenoWorker,
- deno_runtime::deno_permissions::PermissionsContainer::allow_all(),
+ crate::file_fetcher::FetchPermissionsOption::AllowAll,
)
.await?;
}
diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs
index ed3d94c84..24bc8e30a 100644
--- a/cli/tools/repl/mod.rs
+++ b/cli/tools/repl/mod.rs
@@ -16,8 +16,6 @@ use deno_core::error::AnyError;
use deno_core::futures::StreamExt;
use deno_core::serde_json;
use deno_core::unsync::spawn_blocking;
-use deno_runtime::deno_permissions::Permissions;
-use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::WorkerExecutionMode;
use rustyline::error::ReadlineError;
@@ -151,9 +149,7 @@ async fn read_eval_file(
let specifier =
deno_core::resolve_url_or_path(eval_file, cli_options.initial_cwd())?;
- let file = file_fetcher
- .fetch(&specifier, &PermissionsContainer::allow_all())
- .await?;
+ let file = file_fetcher.fetch_bypass_permissions(&specifier).await?;
Ok(file.into_text_decoded()?.source)
}
@@ -166,9 +162,7 @@ pub async fn run(
let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?;
let main_module = cli_options.resolve_main_module()?;
- let permissions = PermissionsContainer::new(Permissions::from_options(
- &cli_options.permissions_options()?,
- )?);
+ let permissions = factory.create_permissions_container()?;
let npm_resolver = factory.npm_resolver().await?.clone();
let resolver = factory.resolver().await?.clone();
let file_fetcher = factory.file_fetcher()?;
diff --git a/cli/tools/run/mod.rs b/cli/tools/run/mod.rs
index bdafdae88..200644490 100644
--- a/cli/tools/run/mod.rs
+++ b/cli/tools/run/mod.rs
@@ -5,8 +5,6 @@ use std::sync::Arc;
use deno_config::deno_json::NodeModulesDirMode;
use deno_core::error::AnyError;
-use deno_runtime::deno_permissions::Permissions;
-use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::WorkerExecutionMode;
use crate::args::EvalFlags;
@@ -62,9 +60,7 @@ pub async fn run_script(
maybe_npm_install(&factory).await?;
- let permissions = PermissionsContainer::new(Permissions::from_options(
- &cli_options.permissions_options()?,
- )?);
+ let permissions = factory.create_permissions_container()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let mut worker = worker_factory
.create_main_worker(mode, main_module, permissions)
@@ -83,9 +79,7 @@ pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> {
let file_fetcher = factory.file_fetcher()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
- let permissions = PermissionsContainer::new(Permissions::from_options(
- &cli_options.permissions_options()?,
- )?);
+ let permissions = factory.create_permissions_container()?;
let mut source = Vec::new();
std::io::stdin().read_to_end(&mut source)?;
// Save a fake file into file fetcher cache
@@ -131,9 +125,7 @@ async fn run_with_watch(
let _ = watcher_communicator.watch_paths(cli_options.watch_paths());
- let permissions = PermissionsContainer::new(Permissions::from_options(
- &cli_options.permissions_options()?,
- )?);
+ let permissions = factory.create_permissions_container()?;
let mut worker = factory
.create_cli_main_worker_factory()
.await?
@@ -181,9 +173,7 @@ pub async fn eval_command(
source: source_code.into_bytes().into(),
});
- let permissions = PermissionsContainer::new(Permissions::from_options(
- &cli_options.permissions_options()?,
- )?);
+ let permissions = factory.create_permissions_container()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let mut worker = worker_factory
.create_main_worker(WorkerExecutionMode::Eval, main_module, permissions)
diff --git a/cli/tools/serve.rs b/cli/tools/serve.rs
index 24666b8f6..2f553cf1e 100644
--- a/cli/tools/serve.rs
+++ b/cli/tools/serve.rs
@@ -5,7 +5,6 @@ use std::sync::Arc;
use deno_core::error::AnyError;
use deno_core::futures::TryFutureExt;
use deno_core::ModuleSpecifier;
-use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use super::run::check_permission_before_script;
@@ -45,9 +44,7 @@ pub async fn serve(
maybe_npm_install(&factory).await?;
- let permissions = PermissionsContainer::new(Permissions::from_options(
- &cli_options.permissions_options()?,
- )?);
+ let permissions = factory.create_permissions_container()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
do_serve(
@@ -175,9 +172,7 @@ async fn serve_with_watch(
let _ = watcher_communicator.watch_paths(cli_options.watch_paths());
- let permissions = PermissionsContainer::new(Permissions::from_options(
- &cli_options.permissions_options()?,
- )?);
+ let permissions = factory.create_permissions_container()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
do_serve(worker_factory, main_module, permissions, worker_count, hmr)
diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs
index 7b172cf87..63382ffc6 100644
--- a/cli/tools/test/mod.rs
+++ b/cli/tools/test/mod.rs
@@ -56,6 +56,7 @@ use deno_runtime::deno_io::StdioPipe;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::fmt_errors::format_js_error;
+use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::tokio_util::create_and_run_current_thread;
use deno_runtime::worker::MainWorker;
use deno_runtime::WorkerExecutionMode;
@@ -595,7 +596,7 @@ fn get_test_reporter(options: &TestSpecifiersOptions) -> Box<dyn TestReporter> {
async fn configure_main_worker(
worker_factory: Arc<CliMainWorkerFactory>,
specifier: &Url,
- permissions: Permissions,
+ permissions_container: PermissionsContainer,
worker_sender: TestEventWorkerSender,
options: &TestSpecifierOptions,
) -> Result<(Option<Box<dyn CoverageCollector>>, MainWorker), anyhow::Error> {
@@ -603,7 +604,7 @@ async fn configure_main_worker(
.create_custom_worker(
WorkerExecutionMode::Test,
specifier.clone(),
- PermissionsContainer::new(permissions),
+ permissions_container,
vec![ops::testing::deno_test::init_ops(worker_sender.sender)],
Stdio {
stdin: StdioPipe::inherit(),
@@ -646,7 +647,7 @@ async fn configure_main_worker(
/// both.
pub async fn test_specifier(
worker_factory: Arc<CliMainWorkerFactory>,
- permissions: Permissions,
+ permissions_container: PermissionsContainer,
specifier: ModuleSpecifier,
worker_sender: TestEventWorkerSender,
fail_fast_tracker: FailFastTracker,
@@ -658,7 +659,7 @@ pub async fn test_specifier(
let (coverage_collector, mut worker) = configure_main_worker(
worker_factory,
&specifier,
- permissions,
+ permissions_container,
worker_sender,
&options,
)
@@ -1327,9 +1328,8 @@ async fn fetch_inline_files(
) -> Result<Vec<File>, AnyError> {
let mut files = Vec::new();
for specifier in specifiers {
- let fetch_permissions = PermissionsContainer::allow_all();
let file = file_fetcher
- .fetch(&specifier, &fetch_permissions)
+ .fetch_bypass_permissions(&specifier)
.await?
.into_text_decoded()?;
@@ -1407,6 +1407,7 @@ static HAS_TEST_RUN_SIGINT_HANDLER: AtomicBool = AtomicBool::new(false);
async fn test_specifiers(
worker_factory: Arc<CliMainWorkerFactory>,
permissions: &Permissions,
+ permission_desc_parser: &Arc<RuntimePermissionDescriptorParser>,
specifiers: Vec<ModuleSpecifier>,
options: TestSpecifiersOptions,
) -> Result<(), AnyError> {
@@ -1434,14 +1435,17 @@ async fn test_specifiers(
let join_handles = specifiers.into_iter().map(move |specifier| {
let worker_factory = worker_factory.clone();
- let permissions = permissions.clone();
+ let permissions_container = PermissionsContainer::new(
+ permission_desc_parser.clone(),
+ permissions.clone(),
+ );
let worker_sender = test_event_sender_factory.worker();
let fail_fast_tracker = fail_fast_tracker.clone();
let specifier_options = options.specifier.clone();
spawn_blocking(move || {
create_and_run_current_thread(test_specifier(
worker_factory,
- permissions,
+ permissions_container,
specifier,
worker_sender,
fail_fast_tracker,
@@ -1739,9 +1743,7 @@ async fn fetch_specifiers_with_test_mode(
.collect::<Vec<_>>();
for (specifier, mode) in &mut specifiers_with_mode {
- let file = file_fetcher
- .fetch(specifier, &PermissionsContainer::allow_all())
- .await?;
+ let file = file_fetcher.fetch_bypass_permissions(specifier).await?;
let (media_type, _) = file.resolve_media_type_and_charset();
if matches!(media_type, MediaType::Unknown | MediaType::Dts) {
@@ -1764,8 +1766,11 @@ pub async fn run_tests(
// Various test files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
- let permissions =
- Permissions::from_options(&cli_options.permissions_options()?)?;
+ let permission_desc_parser = factory.permission_desc_parser()?;
+ let permissions = Permissions::from_options(
+ permission_desc_parser.as_ref(),
+ &cli_options.permissions_options(),
+ )?;
let log_level = cli_options.log_level();
let members_with_test_options =
@@ -1802,6 +1807,7 @@ pub async fn run_tests(
test_specifiers(
worker_factory,
&permissions,
+ permission_desc_parser,
specifiers_with_mode
.into_iter()
.filter_map(|(s, m)| match m {
@@ -1914,8 +1920,11 @@ pub async fn run_tests_with_watch(
.flatten()
.collect::<Vec<_>>();
- let permissions =
- Permissions::from_options(&cli_options.permissions_options()?)?;
+ let permission_desc_parser = factory.permission_desc_parser()?;
+ let permissions = Permissions::from_options(
+ permission_desc_parser.as_ref(),
+ &cli_options.permissions_options(),
+ )?;
let graph = module_graph_creator
.create_graph(graph_kind, test_modules)
.await?;
@@ -1969,6 +1978,7 @@ pub async fn run_tests_with_watch(
test_specifiers(
worker_factory,
&permissions,
+ permission_desc_parser,
specifiers_with_mode
.into_iter()
.filter_map(|(s, m)| match m {
diff --git a/cli/worker.rs b/cli/worker.rs
index 94884ff82..78753bf22 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -30,6 +30,7 @@ use deno_runtime::deno_web::BlobStore;
use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::ops::worker_host::CreateWebWorkerCb;
+use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::web_worker::WebWorker;
use deno_runtime::web_worker::WebWorkerOptions;
use deno_runtime::worker::MainWorker;
@@ -121,23 +122,24 @@ pub struct CliMainWorkerOptions {
}
struct SharedWorkerState {
- options: CliMainWorkerOptions,
- subcommand: DenoSubcommand,
- storage_key_resolver: StorageKeyResolver,
- npm_resolver: Arc<dyn CliNpmResolver>,
- node_resolver: Arc<NodeResolver>,
blob_store: Arc<BlobStore>,
broadcast_channel: InMemoryBroadcastChannel,
- shared_array_buffer_store: SharedArrayBufferStore,
+ code_cache: Option<Arc<dyn code_cache::CodeCache>>,
compiled_wasm_module_store: CompiledWasmModuleStore,
- module_loader_factory: Box<dyn ModuleLoaderFactory>,
- root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
+ feature_checker: Arc<FeatureChecker>,
fs: Arc<dyn deno_fs::FileSystem>,
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
maybe_inspector_server: Option<Arc<InspectorServer>>,
maybe_lockfile: Option<Arc<CliLockfile>>,
- feature_checker: Arc<FeatureChecker>,
- code_cache: Option<Arc<dyn code_cache::CodeCache>>,
+ module_loader_factory: Box<dyn ModuleLoaderFactory>,
+ node_resolver: Arc<NodeResolver>,
+ npm_resolver: Arc<dyn CliNpmResolver>,
+ permission_desc_parser: Arc<RuntimePermissionDescriptorParser>,
+ root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
+ shared_array_buffer_store: SharedArrayBufferStore,
+ storage_key_resolver: StorageKeyResolver,
+ options: CliMainWorkerOptions,
+ subcommand: DenoSubcommand,
}
impl SharedWorkerState {
@@ -418,40 +420,42 @@ pub struct CliMainWorkerFactory {
impl CliMainWorkerFactory {
#[allow(clippy::too_many_arguments)]
pub fn new(
- storage_key_resolver: StorageKeyResolver,
- subcommand: DenoSubcommand,
- npm_resolver: Arc<dyn CliNpmResolver>,
- node_resolver: Arc<NodeResolver>,
blob_store: Arc<BlobStore>,
- module_loader_factory: Box<dyn ModuleLoaderFactory>,
- root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
+ code_cache: Option<Arc<dyn code_cache::CodeCache>>,
+ feature_checker: Arc<FeatureChecker>,
fs: Arc<dyn deno_fs::FileSystem>,
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
maybe_inspector_server: Option<Arc<InspectorServer>>,
maybe_lockfile: Option<Arc<CliLockfile>>,
- feature_checker: Arc<FeatureChecker>,
- code_cache: Option<Arc<dyn code_cache::CodeCache>>,
+ module_loader_factory: Box<dyn ModuleLoaderFactory>,
+ node_resolver: Arc<NodeResolver>,
+ npm_resolver: Arc<dyn CliNpmResolver>,
+ permission_parser: Arc<RuntimePermissionDescriptorParser>,
+ root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
+ storage_key_resolver: StorageKeyResolver,
+ subcommand: DenoSubcommand,
options: CliMainWorkerOptions,
) -> Self {
Self {
shared: Arc::new(SharedWorkerState {
- options,
- subcommand,
- storage_key_resolver,
- npm_resolver,
- node_resolver,
blob_store,
broadcast_channel: Default::default(),
- shared_array_buffer_store: Default::default(),
+ code_cache,
compiled_wasm_module_store: Default::default(),
- module_loader_factory,
- root_cert_store_provider,
+ feature_checker,
fs,
maybe_file_watcher_communicator,
maybe_inspector_server,
maybe_lockfile,
- feature_checker,
- code_cache,
+ module_loader_factory,
+ node_resolver,
+ npm_resolver,
+ permission_desc_parser: permission_parser,
+ root_cert_store_provider,
+ shared_array_buffer_store: Default::default(),
+ storage_key_resolver,
+ options,
+ subcommand,
}),
}
}
@@ -525,9 +529,13 @@ impl CliMainWorkerFactory {
(main_module, false)
};
- let ModuleLoaderAndSourceMapGetter { module_loader } = shared
- .module_loader_factory
- .create_for_main(PermissionsContainer::allow_all(), permissions.clone());
+ let ModuleLoaderAndSourceMapGetter { module_loader } =
+ shared.module_loader_factory.create_for_main(
+ PermissionsContainer::allow_all(
+ self.shared.permission_desc_parser.clone(),
+ ),
+ permissions.clone(),
+ );
let maybe_inspector_server = shared.maybe_inspector_server.clone();
let create_web_worker_cb =
@@ -619,6 +627,7 @@ impl CliMainWorkerFactory {
),
stdio,
feature_checker,
+ permission_desc_parser: shared.permission_desc_parser.clone(),
skip_op_registration: shared.options.skip_op_registration,
v8_code_cache: shared.code_cache.clone(),
};
@@ -809,6 +818,7 @@ fn create_web_worker_callback(
stdio: stdio.clone(),
cache_storage_dir,
feature_checker,
+ permission_desc_parser: shared.permission_desc_parser.clone(),
strace_ops: shared.options.strace_ops.clone(),
close_on_idle: args.close_on_idle,
maybe_worker_metadata: args.maybe_worker_metadata,
@@ -830,13 +840,16 @@ fn create_web_worker_callback(
mod tests {
use super::*;
use deno_core::resolve_path;
+ use deno_fs::RealFs;
use deno_runtime::deno_permissions::Permissions;
fn create_test_worker() -> MainWorker {
let main_module =
resolve_path("./hello.js", &std::env::current_dir().unwrap()).unwrap();
- let permissions =
- PermissionsContainer::new(Permissions::none_without_prompt());
+ let permissions = PermissionsContainer::new(
+ Arc::new(RuntimePermissionDescriptorParser::new(Arc::new(RealFs))),
+ Permissions::none_without_prompt(),
+ );
let options = WorkerOptions {
startup_snapshot: crate::js::deno_isolate_init(),
diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs
index 79659771e..88f303852 100644
--- a/ext/fetch/lib.rs
+++ b/ext/fetch/lib.rs
@@ -299,10 +299,15 @@ impl Drop for ResourceToBodyAdapter {
pub trait FetchPermissions {
fn check_net_url(
&mut self,
- _url: &Url,
+ url: &Url,
api_name: &str,
) -> Result<(), AnyError>;
- fn check_read(&mut self, _p: &Path, api_name: &str) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read<'a>(
+ &mut self,
+ p: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
}
impl FetchPermissions for deno_permissions::PermissionsContainer {
@@ -316,12 +321,16 @@ impl FetchPermissions for deno_permissions::PermissionsContainer {
}
#[inline(always)]
- fn check_read(
+ fn check_read<'a>(
&mut self,
- path: &Path,
+ path: &'a Path,
api_name: &str,
- ) -> Result<(), AnyError> {
- deno_permissions::PermissionsContainer::check_read(self, path, api_name)
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_read_path(
+ self,
+ path,
+ Some(api_name),
+ )
}
}
@@ -359,7 +368,11 @@ where
type_error("NetworkError when attempting to fetch resource")
})?;
let permissions = state.borrow_mut::<FP>();
- permissions.check_read(&path, "fetch()")?;
+ let path = permissions.check_read(&path, "fetch()")?;
+ let url = match path {
+ Cow::Owned(path) => Url::from_file_path(path).unwrap(),
+ Cow::Borrowed(_) => url,
+ };
if method != Method::GET {
return Err(type_error(format!(
diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs
index 380fc03a1..3572b9e81 100644
--- a/ext/ffi/call.rs
+++ b/ext/ffi/call.rs
@@ -287,7 +287,7 @@ where
{
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
};
let symbol = PtrSymbol::new(pointer, &def)?;
@@ -384,7 +384,7 @@ where
{
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
};
let symbol = PtrSymbol::new(pointer, &def)?;
diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs
index 7d0114131..6fa166f52 100644
--- a/ext/ffi/callback.rs
+++ b/ext/ffi/callback.rs
@@ -557,7 +557,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
let thread_id: u32 = LOCAL_THREAD_ID.with(|s| {
let value = *s.borrow();
diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs
index 2bae5d223..10199bf85 100644
--- a/ext/ffi/dlfcn.rs
+++ b/ext/ffi/dlfcn.rs
@@ -19,7 +19,6 @@ use serde_value::ValueDeserializer;
use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::c_void;
-use std::path::PathBuf;
use std::rc::Rc;
pub struct DynamicLibraryResource {
@@ -121,15 +120,13 @@ pub fn op_ffi_load<'scope, FP>(
where
FP: FfiPermissions + 'static,
{
- let path = args.path;
-
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(Some(&PathBuf::from(&path)))?;
+ let path = permissions.check_partial_with_path(&args.path)?;
let lib = Library::open(&path).map_err(|e| {
dlopen2::Error::OpeningLibraryError(std::io::Error::new(
std::io::ErrorKind::Other,
- format_error(e, path),
+ format_error(e, &path),
))
})?;
let mut resource = DynamicLibraryResource {
@@ -290,7 +287,10 @@ fn sync_fn_impl<'s>(
// `path` is only used on Windows.
#[allow(unused_variables)]
-pub(crate) fn format_error(e: dlopen2::Error, path: String) -> String {
+pub(crate) fn format_error(
+ e: dlopen2::Error,
+ path: &std::path::Path,
+) -> String {
match e {
#[cfg(target_os = "windows")]
// This calls FormatMessageW with library path
@@ -300,7 +300,6 @@ pub(crate) fn format_error(e: dlopen2::Error, path: String) -> String {
//
// https://github.com/denoland/deno/issues/11632
dlopen2::Error::OpeningLibraryError(e) => {
- use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use winapi::shared::minwindef::DWORD;
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
@@ -324,7 +323,8 @@ pub(crate) fn format_error(e: dlopen2::Error, path: String) -> String {
let mut buf = vec![0; 500];
- let path = OsStr::new(&path)
+ let path = path
+ .as_os_str()
.encode_wide()
.chain(Some(0))
.collect::<Vec<_>>();
@@ -384,7 +384,7 @@ mod tests {
std::io::Error::from_raw_os_error(0x000000C1),
);
assert_eq!(
- format_error(err, "foo.dll".to_string()),
+ format_error(err, &std::path::PathBuf::from("foo.dll")),
"foo.dll is not a valid Win32 application.\r\n".to_string(),
);
}
diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs
index 59d241c5a..77ec3c85e 100644
--- a/ext/ffi/lib.rs
+++ b/ext/ffi/lib.rs
@@ -5,7 +5,7 @@ use deno_core::error::AnyError;
use std::mem::size_of;
use std::os::raw::c_char;
use std::os::raw::c_short;
-use std::path::Path;
+use std::path::PathBuf;
mod call;
mod callback;
@@ -41,13 +41,28 @@ const _: () = {
pub const UNSTABLE_FEATURE_NAME: &str = "ffi";
pub trait FfiPermissions {
- fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError>;
+ fn check_partial_no_path(&mut self) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_partial_with_path(
+ &mut self,
+ path: &str,
+ ) -> Result<PathBuf, AnyError>;
}
impl FfiPermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
- fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
- deno_permissions::PermissionsContainer::check_ffi_partial(self, path)
+ fn check_partial_no_path(&mut self) -> Result<(), AnyError> {
+ deno_permissions::PermissionsContainer::check_ffi_partial_no_path(self)
+ }
+
+ #[inline(always)]
+ fn check_partial_with_path(
+ &mut self,
+ path: &str,
+ ) -> Result<PathBuf, AnyError> {
+ deno_permissions::PermissionsContainer::check_ffi_partial_with_path(
+ self, path,
+ )
}
}
diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs
index f56537475..315e6d53b 100644
--- a/ext/ffi/repr.rs
+++ b/ext/ffi/repr.rs
@@ -21,7 +21,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
Ok(ptr_number as *mut c_void)
}
@@ -36,7 +36,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
Ok(a == b)
}
@@ -50,7 +50,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
Ok(buf as *mut c_void)
}
@@ -64,7 +64,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
let Some(buf) = buf.get_backing_store() else {
return Ok(0 as _);
@@ -85,7 +85,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid pointer to offset, pointer is null"));
@@ -115,7 +115,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
Ok(ptr as usize)
}
@@ -132,7 +132,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid ArrayBuffer pointer, pointer is null"));
@@ -164,7 +164,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if src.is_null() {
Err(type_error("Invalid ArrayBuffer pointer, pointer is null"))
@@ -195,7 +195,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid CString pointer, pointer is null"));
@@ -221,7 +221,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid bool pointer, pointer is null"));
@@ -241,7 +241,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid u8 pointer, pointer is null"));
@@ -263,7 +263,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid i8 pointer, pointer is null"));
@@ -285,7 +285,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid u16 pointer, pointer is null"));
@@ -307,7 +307,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid i16 pointer, pointer is null"));
@@ -329,7 +329,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid u32 pointer, pointer is null"));
@@ -349,7 +349,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid i32 pointer, pointer is null"));
@@ -372,7 +372,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid u64 pointer, pointer is null"));
@@ -398,7 +398,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid i64 pointer, pointer is null"));
@@ -421,7 +421,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid f32 pointer, pointer is null"));
@@ -441,7 +441,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid f64 pointer, pointer is null"));
@@ -461,7 +461,7 @@ where
FP: FfiPermissions + 'static,
{
let permissions = state.borrow_mut::<FP>();
- permissions.check_partial(None)?;
+ permissions.check_partial_no_path()?;
if ptr.is_null() {
return Err(type_error("Invalid pointer pointer, pointer is null"));
diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs
index a6f273323..bd49078b2 100644
--- a/ext/fs/lib.rs
+++ b/ext/fs/lib.rs
@@ -24,6 +24,7 @@ use deno_core::error::AnyError;
use deno_io::fs::FsError;
use std::borrow::Cow;
use std::path::Path;
+use std::path::PathBuf;
pub trait FsPermissions {
fn check_open<'a>(
@@ -34,8 +35,18 @@ pub trait FsPermissions {
path: &'a Path,
api_name: &str,
) -> Result<std::borrow::Cow<'a, Path>, FsError>;
- fn check_read(&mut self, path: &Path, api_name: &str)
- -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read(
+ &mut self,
+ path: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError>;
fn check_read_blind(
&mut self,
@@ -43,16 +54,24 @@ pub trait FsPermissions {
display: &str,
api_name: &str,
) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_write(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError>;
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_write_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_write_partial(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError>;
+ ) -> Result<PathBuf, AnyError>;
fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError>;
fn check_write_blind(
&mut self,
@@ -96,25 +115,44 @@ impl FsPermissions for deno_permissions::PermissionsContainer {
// If somehow read or write aren't specified, use read
let read = read || !write;
+ let mut path: Cow<'a, Path> = Cow::Borrowed(path);
if read {
- FsPermissions::check_read(self, path, api_name)
+ let resolved_path = FsPermissions::check_read_path(self, &path, api_name)
.map_err(|_| FsError::NotCapable("read"))?;
+ if let Cow::Owned(resolved_path) = resolved_path {
+ path = Cow::Owned(resolved_path);
+ }
}
if write {
- FsPermissions::check_write(self, path, api_name)
- .map_err(|_| FsError::NotCapable("write"))?;
+ let resolved_path =
+ FsPermissions::check_write_path(self, &path, api_name)
+ .map_err(|_| FsError::NotCapable("write"))?;
+ if let Cow::Owned(resolved_path) = resolved_path {
+ path = Cow::Owned(resolved_path);
+ }
}
- Ok(Cow::Borrowed(path))
+ Ok(path)
}
fn check_read(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read(self, path, api_name)
}
+ fn check_read_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_read_path(
+ self,
+ path,
+ Some(api_name),
+ )
+ }
fn check_read_blind(
&mut self,
path: &Path,
@@ -128,17 +166,27 @@ impl FsPermissions for deno_permissions::PermissionsContainer {
fn check_write(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write(self, path, api_name)
}
+ fn check_write_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_write_path(
+ self, path, api_name,
+ )
+ }
+
fn check_write_partial(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write_partial(
self, path, api_name,
)
diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs
index f25cd944d..150d3b955 100644
--- a/ext/fs/ops.rs
+++ b/ext/fs/ops.rs
@@ -5,6 +5,7 @@ use std::io;
use std::io::SeekFrom;
use std::path::Path;
use std::path::PathBuf;
+use std::path::StripPrefixError;
use std::rc::Rc;
use deno_core::anyhow::bail;
@@ -105,8 +106,9 @@ pub fn op_fs_chdir<P>(
where
P: FsPermissions + 'static,
{
- let d = PathBuf::from(&directory);
- state.borrow_mut::<P>().check_read(&d, "Deno.chdir()")?;
+ let d = state
+ .borrow_mut::<P>()
+ .check_read(directory, "Deno.chdir()")?;
state
.borrow::<FileSystemRc>()
.chdir(&d)
@@ -188,11 +190,9 @@ pub fn op_fs_mkdir_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
let mode = mode.unwrap_or(0o777) & 0o777;
- state
+ let path = state
.borrow_mut::<P>()
.check_write(&path, "Deno.mkdirSync()")?;
@@ -213,14 +213,12 @@ pub async fn op_fs_mkdir_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
let mode = mode.unwrap_or(0o777) & 0o777;
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.mkdir_async(path.clone(), recursive, mode)
@@ -239,8 +237,7 @@ pub fn op_fs_chmod_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_write(&path, "Deno.chmodSync()")?;
let fs = state.borrow::<FileSystemRc>();
@@ -257,11 +254,10 @@ pub async fn op_fs_chmod_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.chmod_async(path.clone(), mode)
.await
@@ -279,8 +275,7 @@ pub fn op_fs_chown_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_write(&path, "Deno.chownSync()")?;
let fs = state.borrow::<FileSystemRc>();
@@ -299,11 +294,10 @@ pub async fn op_fs_chown_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.chown_async(path.clone(), uid, gid)
.await
@@ -320,11 +314,9 @@ pub fn op_fs_remove_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state
+ let path = state
.borrow_mut::<P>()
- .check_write(&path, "Deno.removeSync()")?;
+ .check_write(path, "Deno.removeSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.remove_sync(&path, recursive)
@@ -342,21 +334,19 @@ pub async fn op_fs_remove_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- if recursive {
+ let path = if recursive {
state
.borrow_mut::<P>()
- .check_write(&path, "Deno.remove()")?;
+ .check_write(&path, "Deno.remove()")?
} else {
state
.borrow_mut::<P>()
- .check_write_partial(&path, "Deno.remove()")?;
- }
+ .check_write_partial(&path, "Deno.remove()")?
+ };
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.remove_async(path.clone(), recursive)
@@ -375,12 +365,9 @@ pub fn op_fs_copy_file_sync<P>(
where
P: FsPermissions + 'static,
{
- let from = PathBuf::from(from);
- let to = PathBuf::from(to);
-
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&from, "Deno.copyFileSync()")?;
- permissions.check_write(&to, "Deno.copyFileSync()")?;
+ let from = permissions.check_read(from, "Deno.copyFileSync()")?;
+ let to = permissions.check_write(to, "Deno.copyFileSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.copy_file_sync(&from, &to)
@@ -398,15 +385,12 @@ pub async fn op_fs_copy_file_async<P>(
where
P: FsPermissions + 'static,
{
- let from = PathBuf::from(from);
- let to = PathBuf::from(to);
-
- let fs = {
+ let (fs, from, to) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&from, "Deno.copyFile()")?;
- permissions.check_write(&to, "Deno.copyFile()")?;
- state.borrow::<FileSystemRc>().clone()
+ let from = permissions.check_read(&from, "Deno.copyFile()")?;
+ let to = permissions.check_write(&to, "Deno.copyFile()")?;
+ (state.borrow::<FileSystemRc>().clone(), from, to)
};
fs.copy_file_async(from.clone(), to.clone())
@@ -425,8 +409,7 @@ pub fn op_fs_stat_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.statSync()")?;
let fs = state.borrow::<FileSystemRc>();
@@ -445,12 +428,11 @@ pub async fn op_fs_stat_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.stat()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = permissions.check_read(&path, "Deno.stat()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
let stat = fs
.stat_async(path.clone())
@@ -468,8 +450,7 @@ pub fn op_fs_lstat_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.lstatSync()")?;
let fs = state.borrow::<FileSystemRc>();
@@ -488,12 +469,11 @@ pub async fn op_fs_lstat_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.lstat()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = permissions.check_read(&path, "Deno.lstat()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
let stat = fs
.lstat_async(path.clone())
@@ -511,11 +491,9 @@ pub fn op_fs_realpath_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
let fs = state.borrow::<FileSystemRc>().clone();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.realPathSync()")?;
+ let path = permissions.check_read(&path, "Deno.realPathSync()")?;
if path.is_relative() {
permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPathSync()")?;
}
@@ -536,18 +514,16 @@ pub async fn op_fs_realpath_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs;
- {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- fs = state.borrow::<FileSystemRc>().clone();
+ let fs = state.borrow::<FileSystemRc>().clone();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.realPath()")?;
+ let path = permissions.check_read(&path, "Deno.realPath()")?;
if path.is_relative() {
permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPath()")?;
}
- }
+ (fs, path)
+ };
let resolved_path = fs
.realpath_async(path.clone())
.await
@@ -566,9 +542,7 @@ pub fn op_fs_read_dir_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.readDirSync()")?;
@@ -587,14 +561,12 @@ pub async fn op_fs_read_dir_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.readDir()")?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
let entries = fs
@@ -614,13 +586,10 @@ pub fn op_fs_rename_sync<P>(
where
P: FsPermissions + 'static,
{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.renameSync()")?;
- permissions.check_write(&oldpath, "Deno.renameSync()")?;
- permissions.check_write(&newpath, "Deno.renameSync()")?;
+ let _ = permissions.check_read(&oldpath, "Deno.renameSync()")?;
+ let oldpath = permissions.check_write(&oldpath, "Deno.renameSync()")?;
+ let newpath = permissions.check_write(&newpath, "Deno.renameSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.rename_sync(&oldpath, &newpath)
@@ -638,16 +607,13 @@ pub async fn op_fs_rename_async<P>(
where
P: FsPermissions + 'static,
{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
- let fs = {
+ let (fs, oldpath, newpath) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.rename()")?;
- permissions.check_write(&oldpath, "Deno.rename()")?;
- permissions.check_write(&newpath, "Deno.rename()")?;
- state.borrow::<FileSystemRc>().clone()
+ _ = permissions.check_read(&oldpath, "Deno.rename()")?;
+ let oldpath = permissions.check_write(&oldpath, "Deno.rename()")?;
+ let newpath = permissions.check_write(&newpath, "Deno.rename()")?;
+ (state.borrow::<FileSystemRc>().clone(), oldpath, newpath)
};
fs.rename_async(oldpath.clone(), newpath.clone())
@@ -666,14 +632,11 @@ pub fn op_fs_link_sync<P>(
where
P: FsPermissions + 'static,
{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.linkSync()")?;
- permissions.check_write(&oldpath, "Deno.linkSync()")?;
- permissions.check_read(&newpath, "Deno.linkSync()")?;
- permissions.check_write(&newpath, "Deno.linkSync()")?;
+ _ = permissions.check_read(oldpath, "Deno.linkSync()")?;
+ let oldpath = permissions.check_write(oldpath, "Deno.linkSync()")?;
+ _ = permissions.check_read(newpath, "Deno.linkSync()")?;
+ let newpath = permissions.check_write(newpath, "Deno.linkSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.link_sync(&oldpath, &newpath)
@@ -691,17 +654,14 @@ pub async fn op_fs_link_async<P>(
where
P: FsPermissions + 'static,
{
- let oldpath = PathBuf::from(&oldpath);
- let newpath = PathBuf::from(&newpath);
-
- let fs = {
+ let (fs, oldpath, newpath) = {
let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.link()")?;
- permissions.check_write(&oldpath, "Deno.link()")?;
- permissions.check_read(&newpath, "Deno.link()")?;
- permissions.check_write(&newpath, "Deno.link()")?;
- state.borrow::<FileSystemRc>().clone()
+ _ = permissions.check_read(&oldpath, "Deno.link()")?;
+ let oldpath = permissions.check_write(&oldpath, "Deno.link()")?;
+ _ = permissions.check_read(&newpath, "Deno.link()")?;
+ let newpath = permissions.check_write(&newpath, "Deno.link()")?;
+ (state.borrow::<FileSystemRc>().clone(), oldpath, newpath)
};
fs.link_async(oldpath.clone(), newpath.clone())
@@ -772,9 +732,7 @@ pub fn op_fs_read_link_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.readLink()")?;
@@ -794,14 +752,12 @@ pub async fn op_fs_read_link_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_read(&path, "Deno.readLink()")?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
let target = fs
@@ -821,11 +777,9 @@ pub fn op_fs_truncate_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state
+ let path = state
.borrow_mut::<P>()
- .check_write(&path, "Deno.truncateSync()")?;
+ .check_write(path, "Deno.truncateSync()")?;
let fs = state.borrow::<FileSystemRc>();
fs.truncate_sync(&path, len)
@@ -843,14 +797,12 @@ pub async fn op_fs_truncate_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_write(&path, "Deno.truncate()")?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.truncate_async(path.clone(), len)
@@ -872,9 +824,7 @@ pub fn op_fs_utime_sync<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
+ let path = state.borrow_mut::<P>().check_write(path, "Deno.utime()")?;
let fs = state.borrow::<FileSystemRc>();
fs.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
@@ -895,12 +845,10 @@ pub async fn op_fs_utime_async<P>(
where
P: FsPermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
- state.borrow::<FileSystemRc>().clone()
+ let path = state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.utime_async(
@@ -920,15 +868,18 @@ where
#[string]
pub fn op_fs_make_temp_dir_sync<P>(
state: &mut OpState,
- #[string] dir: Option<String>,
+ #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
) -> Result<String, AnyError>
where
P: FsPermissions + 'static,
{
- let (dir, fs) =
- make_temp_check_sync::<P>(state, dir, "Deno.makeTempDirSync()")?;
+ let (dir, fs) = make_temp_check_sync::<P>(
+ state,
+ dir_arg.as_deref(),
+ "Deno.makeTempDirSync()",
+ )?;
let mut rng = thread_rng();
@@ -936,7 +887,11 @@ where
for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.mkdir_sync(&path, false, 0o700) {
- Ok(_) => return path_into_string(path.into_os_string()),
+ Ok(_) => {
+ // PERMISSIONS: ensure the absolute path is not leaked
+ let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?;
+ return path_into_string(path.into_os_string());
+ }
Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
@@ -955,14 +910,18 @@ where
#[string]
pub async fn op_fs_make_temp_dir_async<P>(
state: Rc<RefCell<OpState>>,
- #[string] dir: Option<String>,
+ #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
) -> Result<String, AnyError>
where
P: FsPermissions + 'static,
{
- let (dir, fs) = make_temp_check_async::<P>(state, dir, "Deno.makeTempDir()")?;
+ let (dir, fs) = make_temp_check_async::<P>(
+ state,
+ dir_arg.as_deref(),
+ "Deno.makeTempDir()",
+ )?;
let mut rng = thread_rng();
@@ -970,7 +929,11 @@ where
for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.clone().mkdir_async(path.clone(), false, 0o700).await {
- Ok(_) => return path_into_string(path.into_os_string()),
+ Ok(_) => {
+ // PERMISSIONS: ensure the absolute path is not leaked
+ let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?;
+ return path_into_string(path.into_os_string());
+ }
Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
@@ -989,15 +952,18 @@ where
#[string]
pub fn op_fs_make_temp_file_sync<P>(
state: &mut OpState,
- #[string] dir: Option<String>,
+ #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
) -> Result<String, AnyError>
where
P: FsPermissions + 'static,
{
- let (dir, fs) =
- make_temp_check_sync::<P>(state, dir, "Deno.makeTempFileSync()")?;
+ let (dir, fs) = make_temp_check_sync::<P>(
+ state,
+ dir_arg.as_deref(),
+ "Deno.makeTempFileSync()",
+ )?;
let open_opts = OpenOptions {
write: true,
@@ -1011,7 +977,11 @@ where
for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.open_sync(&path, open_opts, None) {
- Ok(_) => return path_into_string(path.into_os_string()),
+ Ok(_) => {
+ // PERMISSIONS: ensure the absolute path is not leaked
+ let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?;
+ return path_into_string(path.into_os_string());
+ }
Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
@@ -1030,15 +1000,18 @@ where
#[string]
pub async fn op_fs_make_temp_file_async<P>(
state: Rc<RefCell<OpState>>,
- #[string] dir: Option<String>,
+ #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
) -> Result<String, AnyError>
where
P: FsPermissions + 'static,
{
- let (dir, fs) =
- make_temp_check_async::<P>(state, dir, "Deno.makeTempFile()")?;
+ let (dir, fs) = make_temp_check_async::<P>(
+ state,
+ dir_arg.as_deref(),
+ "Deno.makeTempFile()",
+ )?;
let open_opts = OpenOptions {
write: true,
@@ -1053,7 +1026,11 @@ where
for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.clone().open_async(path.clone(), open_opts, None).await {
- Ok(_) => return path_into_string(path.into_os_string()),
+ Ok(_) => {
+ // PERMISSIONS: ensure the absolute path is not leaked
+ let path = strip_dir_prefix(&dir, dir_arg.as_deref(), path)?;
+ return path_into_string(path.into_os_string());
+ }
Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
@@ -1067,9 +1044,26 @@ where
.context("tmpfile")
}
+fn strip_dir_prefix(
+ resolved_dir: &Path,
+ dir_arg: Option<&str>,
+ result_path: PathBuf,
+) -> Result<PathBuf, StripPrefixError> {
+ if resolved_dir.is_absolute() {
+ match &dir_arg {
+ Some(dir_arg) => {
+ Ok(Path::new(dir_arg).join(result_path.strip_prefix(resolved_dir)?))
+ }
+ None => Ok(result_path),
+ }
+ } else {
+ Ok(result_path)
+ }
+}
+
fn make_temp_check_sync<P>(
state: &mut OpState,
- dir: Option<String>,
+ dir: Option<&str>,
api_name: &str,
) -> Result<(PathBuf, FileSystemRc), AnyError>
where
@@ -1077,11 +1071,7 @@ where
{
let fs = state.borrow::<FileSystemRc>().clone();
let dir = match dir {
- Some(dir) => {
- let dir = PathBuf::from(dir);
- state.borrow_mut::<P>().check_write(&dir, api_name)?;
- dir
- }
+ Some(dir) => state.borrow_mut::<P>().check_write(dir, api_name)?,
None => {
let dir = fs.tmp_dir().context("tmpdir")?;
state
@@ -1095,7 +1085,7 @@ where
fn make_temp_check_async<P>(
state: Rc<RefCell<OpState>>,
- dir: Option<String>,
+ dir: Option<&str>,
api_name: &str,
) -> Result<(PathBuf, FileSystemRc), AnyError>
where
@@ -1104,11 +1094,7 @@ where
let mut state = state.borrow_mut();
let fs = state.borrow::<FileSystemRc>().clone();
let dir = match dir {
- Some(dir) => {
- let dir = PathBuf::from(dir);
- state.borrow_mut::<P>().check_write(&dir, api_name)?;
- dir
- }
+ Some(dir) => state.borrow_mut::<P>().check_write(dir, api_name)?,
None => {
let dir = fs.tmp_dir().context("tmpdir")?;
state
diff --git a/ext/kv/sqlite.rs b/ext/kv/sqlite.rs
index b31fd1736..8027ff03d 100644
--- a/ext/kv/sqlite.rs
+++ b/ext/kv/sqlite.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::env::current_dir;
@@ -36,19 +37,37 @@ pub struct SqliteDbHandler<P: SqliteDbHandlerPermissions + 'static> {
}
pub trait SqliteDbHandlerPermissions {
- fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
- fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read(
+ &mut self,
+ p: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_write<'a>(
+ &mut self,
+ p: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
}
impl SqliteDbHandlerPermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
- fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError> {
+ fn check_read(
+ &mut self,
+ p: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read(self, p, api_name)
}
#[inline(always)]
- fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError> {
- deno_permissions::PermissionsContainer::check_write(self, p, api_name)
+ fn check_write<'a>(
+ &mut self,
+ p: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_write_path(self, p, api_name)
}
}
@@ -74,28 +93,35 @@ impl<P: SqliteDbHandlerPermissions> DatabaseHandler for SqliteDbHandler<P> {
state: Rc<RefCell<OpState>>,
path: Option<String>,
) -> Result<Self::DB, AnyError> {
- // Validate path
- if let Some(path) = &path {
- if path != ":memory:" {
- if path.is_empty() {
- return Err(type_error("Filename cannot be empty"));
- }
- if path.starts_with(':') {
- return Err(type_error(
- "Filename cannot start with ':' unless prefixed with './'",
- ));
- }
- let path = Path::new(path);
- {
- let mut state = state.borrow_mut();
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(path, "Deno.openKv")?;
- permissions.check_write(path, "Deno.openKv")?;
- }
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn validate_path<P: SqliteDbHandlerPermissions + 'static>(
+ state: &RefCell<OpState>,
+ path: Option<String>,
+ ) -> Result<Option<String>, AnyError> {
+ let Some(path) = path else {
+ return Ok(None);
+ };
+ if path == ":memory:" {
+ return Ok(Some(path));
+ }
+ if path.is_empty() {
+ return Err(type_error("Filename cannot be empty"));
+ }
+ if path.starts_with(':') {
+ return Err(type_error(
+ "Filename cannot start with ':' unless prefixed with './'",
+ ));
+ }
+ {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ let path = permissions.check_read(&path, "Deno.openKv")?;
+ let path = permissions.check_write(&path, "Deno.openKv")?;
+ Ok(Some(path.to_string_lossy().to_string()))
}
}
- let path = path.clone();
+ let path = validate_path::<P>(&state, path)?;
let default_storage_dir = self.default_storage_dir.clone();
type ConnGen =
Arc<dyn Fn() -> rusqlite::Result<rusqlite::Connection> + Send + Sync>;
diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs
index c9dc62a8b..4500c66fd 100644
--- a/ext/napi/lib.rs
+++ b/ext/napi/lib.rs
@@ -16,7 +16,6 @@ use deno_core::OpState;
use deno_core::V8CrossThreadTaskSpawner;
use std::cell::RefCell;
use std::collections::HashMap;
-use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
use std::thread_local;
@@ -482,15 +481,15 @@ deno_core::extension!(deno_napi,
);
pub trait NapiPermissions {
- fn check(&mut self, path: Option<&Path>)
- -> std::result::Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check(&mut self, path: &str) -> std::result::Result<PathBuf, AnyError>;
}
// NOTE(bartlomieju): for now, NAPI uses `--allow-ffi` flag, but that might
// change in the future.
impl NapiPermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
- fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
+ fn check(&mut self, path: &str) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_ffi(self, path)
}
}
@@ -501,7 +500,7 @@ unsafe impl Send for NapiModuleHandle {}
struct NapiModuleHandle(*const NapiModule);
static NAPI_LOADED_MODULES: std::sync::LazyLock<
- RwLock<HashMap<String, NapiModuleHandle>>,
+ RwLock<HashMap<PathBuf, NapiModuleHandle>>,
> = std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
#[op2(reentrant)]
@@ -519,15 +518,16 @@ where
{
// We must limit the OpState borrow because this function can trigger a
// re-borrow through the NAPI module.
- let (async_work_sender, cleanup_hooks, external_ops_tracker) = {
+ let (async_work_sender, cleanup_hooks, external_ops_tracker, path) = {
let mut op_state = op_state.borrow_mut();
let permissions = op_state.borrow_mut::<NP>();
- permissions.check(Some(&PathBuf::from(&path)))?;
+ let path = permissions.check(&path)?;
let napi_state = op_state.borrow::<NapiState>();
(
op_state.borrow::<V8CrossThreadTaskSpawner>().clone(),
napi_state.env_cleanup_hooks.clone(),
op_state.external_ops_tracker.clone(),
+ path,
)
};
@@ -612,7 +612,7 @@ where
} else {
return Err(type_error(format!(
"Unable to find register Node-API module at {}",
- path
+ path.display()
)));
};
diff --git a/ext/net/lib.rs b/ext/net/lib.rs
index 098d220db..0ef3e85c4 100644
--- a/ext/net/lib.rs
+++ b/ext/net/lib.rs
@@ -13,6 +13,7 @@ use deno_core::error::AnyError;
use deno_core::OpState;
use deno_tls::rustls::RootCertStore;
use deno_tls::RootCertStoreProvider;
+use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
@@ -22,12 +23,27 @@ pub const UNSTABLE_FEATURE_NAME: &str = "net";
pub trait NetPermissions {
fn check_net<T: AsRef<str>>(
&mut self,
- _host: &(T, Option<u16>),
- _api_name: &str,
+ host: &(T, Option<u16>),
+ api_name: &str,
) -> Result<(), AnyError>;
- fn check_read(&mut self, _p: &Path, _api_name: &str) -> Result<(), AnyError>;
- fn check_write(&mut self, _p: &Path, _api_name: &str)
- -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read(
+ &mut self,
+ p: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_write(
+ &mut self,
+ p: &str,
+ api_name: &str,
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_write_path<'a>(
+ &mut self,
+ p: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError>;
}
impl NetPermissions for deno_permissions::PermissionsContainer {
@@ -43,20 +59,31 @@ impl NetPermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
fn check_read(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read(self, path, api_name)
}
#[inline(always)]
fn check_write(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write(self, path, api_name)
}
+
+ #[inline(always)]
+ fn check_write_path<'a>(
+ &mut self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_write_path(
+ self, path, api_name,
+ )
+ }
}
/// Helper for checking unstable features. Used for sync ops.
diff --git a/ext/net/ops.rs b/ext/net/ops.rs
index b74dc8d75..67d32fe03 100644
--- a/ext/net/ops.rs
+++ b/ext/net/ops.rs
@@ -784,6 +784,7 @@ mod tests {
use std::net::Ipv6Addr;
use std::net::ToSocketAddrs;
use std::path::Path;
+ use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;
use trust_dns_proto::rr::rdata::a::A;
@@ -991,18 +992,26 @@ mod tests {
fn check_read(
&mut self,
- _p: &Path,
+ p: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
- Ok(())
+ ) -> Result<PathBuf, AnyError> {
+ Ok(PathBuf::from(p))
}
fn check_write(
&mut self,
- _p: &Path,
+ p: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
- Ok(())
+ ) -> Result<PathBuf, AnyError> {
+ Ok(PathBuf::from(p))
+ }
+
+ fn check_write_path<'a>(
+ &mut self,
+ p: &'a Path,
+ _api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ Ok(Cow::Borrowed(p))
}
}
diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs
index 3ca5adbbe..064da5818 100644
--- a/ext/net/ops_tls.rs
+++ b/ext/net/ops_tls.rs
@@ -52,7 +52,6 @@ use std::io::ErrorKind;
use std::io::Read;
use std::net::SocketAddr;
use std::num::NonZeroUsize;
-use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;
use tokio::io::AsyncReadExt;
@@ -356,15 +355,17 @@ where
.try_borrow::<UnsafelyIgnoreCertificateErrors>()
.and_then(|it| it.0.clone());
- {
+ let cert_file = {
let mut s = state.borrow_mut();
let permissions = s.borrow_mut::<NP>();
permissions
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectTls()")?;
if let Some(path) = cert_file {
- permissions.check_read(Path::new(path), "Deno.connectTls()")?;
+ Some(permissions.check_read(path, "Deno.connectTls()")?)
+ } else {
+ None
}
- }
+ };
let mut ca_certs = args
.ca_certs
diff --git a/ext/net/ops_unix.rs b/ext/net/ops_unix.rs
index 7d2f6af3c..95293284f 100644
--- a/ext/net/ops_unix.rs
+++ b/ext/net/ops_unix.rs
@@ -94,22 +94,22 @@ pub async fn op_net_accept_unix(
#[serde]
pub async fn op_net_connect_unix<NP>(
state: Rc<RefCell<OpState>>,
- #[string] path: String,
+ #[string] address_path: String,
) -> Result<(ResourceId, Option<String>, Option<String>), AnyError>
where
NP: NetPermissions + 'static,
{
- let address_path = Path::new(&path);
- {
+ let address_path = {
let mut state_ = state.borrow_mut();
- state_
+ let address_path = state_
.borrow_mut::<NP>()
- .check_read(address_path, "Deno.connect()")?;
- state_
+ .check_read(&address_path, "Deno.connect()")?;
+ _ = state_
.borrow_mut::<NP>()
- .check_write(address_path, "Deno.connect()")?;
- }
- let unix_stream = UnixStream::connect(Path::new(&path)).await?;
+ .check_write_path(&address_path, "Deno.connect()")?;
+ address_path
+ };
+ let unix_stream = UnixStream::connect(&address_path).await?;
let local_addr = unix_stream.local_addr()?;
let remote_addr = unix_stream.peer_addr()?;
let local_addr_path = local_addr.as_pathname().map(pathstring).transpose()?;
@@ -148,18 +148,17 @@ pub async fn op_net_recv_unixpacket(
pub async fn op_net_send_unixpacket<NP>(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
- #[string] path: String,
+ #[string] address_path: String,
#[buffer] zero_copy: JsBuffer,
) -> Result<usize, AnyError>
where
NP: NetPermissions + 'static,
{
- let address_path = Path::new(&path);
- {
+ let address_path = {
let mut s = state.borrow_mut();
s.borrow_mut::<NP>()
- .check_write(address_path, "Deno.DatagramConn.send()")?;
- }
+ .check_write(&address_path, "Deno.DatagramConn.send()")?
+ };
let resource = state
.borrow()
@@ -178,17 +177,16 @@ where
#[serde]
pub fn op_net_listen_unix<NP>(
state: &mut OpState,
- #[string] path: String,
+ #[string] address_path: String,
#[string] api_name: String,
) -> Result<(ResourceId, Option<String>), AnyError>
where
NP: NetPermissions + 'static,
{
- let address_path = Path::new(&path);
let permissions = state.borrow_mut::<NP>();
let api_call_expr = format!("{}()", api_name);
- permissions.check_read(address_path, &api_call_expr)?;
- permissions.check_write(address_path, &api_call_expr)?;
+ let address_path = permissions.check_read(&address_path, &api_call_expr)?;
+ _ = permissions.check_write_path(&address_path, &api_call_expr)?;
let listener = UnixListener::bind(address_path)?;
let local_addr = listener.local_addr()?;
let pathname = local_addr.as_pathname().map(pathstring).transpose()?;
@@ -199,15 +197,15 @@ where
pub fn net_listen_unixpacket<NP>(
state: &mut OpState,
- path: String,
+ address_path: String,
) -> Result<(ResourceId, Option<String>), AnyError>
where
NP: NetPermissions + 'static,
{
- let address_path = Path::new(&path);
let permissions = state.borrow_mut::<NP>();
- permissions.check_read(address_path, "Deno.listenDatagram()")?;
- permissions.check_write(address_path, "Deno.listenDatagram()")?;
+ let address_path =
+ permissions.check_read(&address_path, "Deno.listenDatagram()")?;
+ _ = permissions.check_write_path(&address_path, "Deno.listenDatagram()")?;
let socket = UnixDatagram::bind(address_path)?;
let local_addr = socket.local_addr()?;
let pathname = local_addr.as_pathname().map(pathstring).transpose()?;
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 75581c128..fd9d4f9af 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -3,8 +3,10 @@
#![deny(clippy::print_stderr)]
#![deny(clippy::print_stdout)]
+use std::borrow::Cow;
use std::collections::HashSet;
use std::path::Path;
+use std::path::PathBuf;
use deno_core::error::AnyError;
use deno_core::located_script_name;
@@ -49,21 +51,29 @@ pub trait NodePermissions {
url: &Url,
api_name: &str,
) -> Result<(), AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)]
- fn check_read(&mut self, path: &Path) -> Result<(), AnyError> {
+ fn check_read(&mut self, path: &str) -> Result<PathBuf, AnyError> {
self.check_read_with_api_name(path, None)
}
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_read_with_api_name(
&mut self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError>;
+ ) -> Result<PathBuf, AnyError>;
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ fn check_read_path<'a>(
+ &mut self,
+ path: &'a Path,
+ ) -> Result<Cow<'a, Path>, 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"]
fn check_write_with_api_name(
&mut self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError>;
+ ) -> Result<PathBuf, AnyError>;
}
impl NodePermissions for deno_permissions::PermissionsContainer {
@@ -79,20 +89,27 @@ impl NodePermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
fn check_read_with_api_name(
&mut self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read_with_api_name(
self, path, api_name,
)
}
+ fn check_read_path<'a>(
+ &mut self,
+ path: &'a Path,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ deno_permissions::PermissionsContainer::check_read_path(self, path, None)
+ }
+
#[inline(always)]
fn check_write_with_api_name(
&mut self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write_with_api_name(
self, path, api_name,
)
diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs
index 687903325..6253f32d0 100644
--- a/ext/node/ops/fs.rs
+++ b/ext/node/ops/fs.rs
@@ -1,8 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::cell::RefCell;
-use std::path::Path;
-use std::path::PathBuf;
use std::rc::Rc;
use deno_core::error::AnyError;
@@ -21,8 +19,7 @@ pub fn op_node_fs_exists_sync<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.existsSync()"))?;
let fs = state.borrow::<FileSystemRc>();
@@ -37,14 +34,12 @@ pub async fn op_node_fs_exists<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.exists()"))?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
Ok(fs.exists_async(path).await?)
@@ -59,18 +54,15 @@ pub fn op_node_cp_sync<P>(
where
P: NodePermissions + 'static,
{
- let path = Path::new(path);
- let new_path = Path::new(new_path);
-
- state
+ let path = state
.borrow_mut::<P>()
.check_read_with_api_name(path, Some("node:fs.cpSync"))?;
- state
+ let new_path = state
.borrow_mut::<P>()
.check_write_with_api_name(new_path, Some("node:fs.cpSync"))?;
let fs = state.borrow::<FileSystemRc>();
- fs.cp_sync(path, new_path)?;
+ fs.cp_sync(&path, &new_path)?;
Ok(())
}
@@ -83,18 +75,15 @@ pub async fn op_node_cp<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
- let new_path = PathBuf::from(new_path);
-
- let fs = {
+ let (fs, path, new_path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.cpSync"))?;
- state
+ let new_path = state
.borrow_mut::<P>()
.check_write_with_api_name(&new_path, Some("node:fs.cpSync"))?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path, new_path)
};
fs.cp_async(path, new_path).await?;
@@ -123,21 +112,21 @@ pub fn op_node_statfs<P>(
where
P: NodePermissions + 'static,
{
- {
+ let path = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
- .check_read_with_api_name(Path::new(&path), Some("node:fs.statfs"))?;
+ .check_read_with_api_name(&path, Some("node:fs.statfs"))?;
state
.borrow_mut::<P>()
.check_sys("statfs", "node:fs.statfs")?;
- }
+ path
+ };
#[cfg(unix)]
{
- use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
- let path = OsStr::new(&path);
+ let path = path.as_os_str();
let mut cpath = path.as_bytes().to_vec();
cpath.push(0);
if bigint {
@@ -196,7 +185,7 @@ where
// Using a vfs here doesn't make sense, it won't align with the windows API
// call below.
#[allow(clippy::disallowed_methods)]
- let path = Path::new(&path).canonicalize()?;
+ let path = path.canonicalize()?;
let root = path
.ancestors()
.last()
@@ -256,14 +245,12 @@ pub fn op_node_lutimes_sync<P>(
where
P: NodePermissions + 'static,
{
- let path = Path::new(path);
-
- state
+ let path = state
.borrow_mut::<P>()
.check_write_with_api_name(path, Some("node:fs.lutimes"))?;
let fs = state.borrow::<FileSystemRc>();
- fs.lutime_sync(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)?;
+ fs.lutime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)?;
Ok(())
}
@@ -279,14 +266,12 @@ pub async fn op_node_lutimes<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
-
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lutimesSync"))?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.lutime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
@@ -305,8 +290,7 @@ pub fn op_node_lchown_sync<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lchownSync"))?;
let fs = state.borrow::<FileSystemRc>();
@@ -324,13 +308,12 @@ pub async fn op_node_lchown<P>(
where
P: NodePermissions + 'static,
{
- let path = PathBuf::from(path);
- let fs = {
+ let (fs, path) = {
let mut state = state.borrow_mut();
- state
+ let path = state
.borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lchown"))?;
- state.borrow::<FileSystemRc>().clone()
+ (state.borrow::<FileSystemRc>().clone(), path)
};
fs.lchown_async(path, uid, gid).await?;
Ok(())
diff --git a/runtime/examples/extension/main.rs b/runtime/examples/extension/main.rs
index 4339bdf67..6f4f02508 100644
--- a/runtime/examples/extension/main.rs
+++ b/runtime/examples/extension/main.rs
@@ -5,12 +5,15 @@
use std::path::Path;
use std::rc::Rc;
+use std::sync::Arc;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::FsModuleLoader;
use deno_core::ModuleSpecifier;
+use deno_fs::RealFs;
use deno_runtime::deno_permissions::PermissionsContainer;
+use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::worker::MainWorker;
use deno_runtime::worker::WorkerOptions;
@@ -34,7 +37,9 @@ async fn main() -> Result<(), AnyError> {
eprintln!("Running {main_module}...");
let mut worker = MainWorker::bootstrap_from_options(
main_module.clone(),
- PermissionsContainer::allow_all(),
+ PermissionsContainer::allow_all(Arc::new(
+ RuntimePermissionDescriptorParser::new(Arc::new(RealFs)),
+ )),
WorkerOptions {
module_loader: Rc::new(FsModuleLoader),
extensions: vec![hello_runtime::init_ops_and_esm()],
diff --git a/runtime/lib.rs b/runtime/lib.rs
index ed3f9fbc6..034c094a0 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -33,6 +33,7 @@ pub mod fs_util;
pub mod inspector_server;
pub mod js;
pub mod ops;
+pub mod permissions;
pub mod snapshot;
pub mod tokio_util;
pub mod web_worker;
diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs
index 58fe9d5fd..d88a32d91 100644
--- a/runtime/ops/fs_events.rs
+++ b/runtime/ops/fs_events.rs
@@ -123,10 +123,9 @@ fn op_fs_events_open(
RecursiveMode::NonRecursive
};
for path in &args.paths {
- let path = PathBuf::from(path);
- state
+ let path = state
.borrow_mut::<PermissionsContainer>()
- .check_read(&path, "Deno.watchFs()")?;
+ .check_read(path, "Deno.watchFs()")?;
watcher.watch(&path, recursive_mode)?;
}
let resource = FsEventsResource {
diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs
index e6974efad..9b46dd019 100644
--- a/runtime/ops/permissions.rs
+++ b/runtime/ops/permissions.rs
@@ -1,7 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use ::deno_permissions::parse_sys_kind;
-use ::deno_permissions::NetDescriptor;
+use ::deno_permissions::PermissionDescriptorParser;
use ::deno_permissions::PermissionState;
use ::deno_permissions::PermissionsContainer;
use deno_core::error::custom_error;
@@ -10,7 +10,7 @@ use deno_core::op2;
use deno_core::OpState;
use serde::Deserialize;
use serde::Serialize;
-use std::path::Path;
+use std::sync::Arc;
deno_core::extension!(
deno_permissions,
@@ -19,6 +19,12 @@ deno_core::extension!(
op_revoke_permission,
op_request_permission,
],
+ options = {
+ permission_desc_parser: Arc<dyn PermissionDescriptorParser>,
+ },
+ state = |state, options| {
+ state.put(options.permission_desc_parser);
+ },
);
#[derive(Deserialize)]
@@ -56,15 +62,37 @@ pub fn op_query_permission(
state: &mut OpState,
#[serde] args: PermissionArgs,
) -> Result<PermissionStatus, AnyError> {
- let permissions = state.borrow::<PermissionsContainer>().0.lock();
+ let permissions_container = state.borrow::<PermissionsContainer>();
+ // todo(dsherret): don't have this function use the properties of
+ // permission container
+ let desc_parser = &permissions_container.descriptor_parser;
+ let permissions = permissions_container.inner.lock();
let path = args.path.as_deref();
let perm = match args.name.as_ref() {
- "read" => permissions.read.query(path.map(Path::new)),
- "write" => permissions.write.query(path.map(Path::new)),
+ "read" => permissions.read.query(
+ path
+ .map(|path| {
+ Result::<_, AnyError>::Ok(
+ desc_parser.parse_path_query(path)?.into_read(),
+ )
+ })
+ .transpose()?
+ .as_ref(),
+ ),
+ "write" => permissions.write.query(
+ path
+ .map(|path| {
+ Result::<_, AnyError>::Ok(
+ desc_parser.parse_path_query(path)?.into_write(),
+ )
+ })
+ .transpose()?
+ .as_ref(),
+ ),
"net" => permissions.net.query(
match args.host.as_deref() {
None => None,
- Some(h) => Some(NetDescriptor::parse(h)?),
+ Some(h) => Some(desc_parser.parse_net_descriptor(h)?),
}
.as_ref(),
),
@@ -72,8 +100,24 @@ pub fn op_query_permission(
"sys" => permissions
.sys
.query(args.kind.as_deref().map(parse_sys_kind).transpose()?),
- "run" => permissions.run.query(args.command.as_deref()),
- "ffi" => permissions.ffi.query(args.path.as_deref().map(Path::new)),
+ "run" => permissions.run.query(
+ args
+ .command
+ .as_deref()
+ .map(|request| desc_parser.parse_run_query(request))
+ .transpose()?
+ .as_ref(),
+ ),
+ "ffi" => permissions.ffi.query(
+ path
+ .map(|path| {
+ Result::<_, AnyError>::Ok(
+ desc_parser.parse_path_query(path)?.into_ffi(),
+ )
+ })
+ .transpose()?
+ .as_ref(),
+ ),
n => {
return Err(custom_error(
"ReferenceError",
@@ -90,15 +134,37 @@ pub fn op_revoke_permission(
state: &mut OpState,
#[serde] args: PermissionArgs,
) -> Result<PermissionStatus, AnyError> {
- let mut permissions = state.borrow_mut::<PermissionsContainer>().0.lock();
+ // todo(dsherret): don't have this function use the properties of
+ // permission container
+ let permissions_container = state.borrow_mut::<PermissionsContainer>();
+ let desc_parser = &permissions_container.descriptor_parser;
+ let mut permissions = permissions_container.inner.lock();
let path = args.path.as_deref();
let perm = match args.name.as_ref() {
- "read" => permissions.read.revoke(path.map(Path::new)),
- "write" => permissions.write.revoke(path.map(Path::new)),
+ "read" => permissions.read.revoke(
+ path
+ .map(|path| {
+ Result::<_, AnyError>::Ok(
+ desc_parser.parse_path_query(path)?.into_read(),
+ )
+ })
+ .transpose()?
+ .as_ref(),
+ ),
+ "write" => permissions.write.revoke(
+ path
+ .map(|path| {
+ Result::<_, AnyError>::Ok(
+ desc_parser.parse_path_query(path)?.into_write(),
+ )
+ })
+ .transpose()?
+ .as_ref(),
+ ),
"net" => permissions.net.revoke(
match args.host.as_deref() {
None => None,
- Some(h) => Some(NetDescriptor::parse(h)?),
+ Some(h) => Some(desc_parser.parse_net_descriptor(h)?),
}
.as_ref(),
),
@@ -106,8 +172,24 @@ pub fn op_revoke_permission(
"sys" => permissions
.sys
.revoke(args.kind.as_deref().map(parse_sys_kind).transpose()?),
- "run" => permissions.run.revoke(args.command.as_deref()),
- "ffi" => permissions.ffi.revoke(args.path.as_deref().map(Path::new)),
+ "run" => permissions.run.revoke(
+ args
+ .command
+ .as_deref()
+ .map(|request| desc_parser.parse_run_query(request))
+ .transpose()?
+ .as_ref(),
+ ),
+ "ffi" => permissions.ffi.revoke(
+ path
+ .map(|path| {
+ Result::<_, AnyError>::Ok(
+ desc_parser.parse_path_query(path)?.into_ffi(),
+ )
+ })
+ .transpose()?
+ .as_ref(),
+ ),
n => {
return Err(custom_error(
"ReferenceError",
@@ -124,15 +206,37 @@ pub fn op_request_permission(
state: &mut OpState,
#[serde] args: PermissionArgs,
) -> Result<PermissionStatus, AnyError> {
- let mut permissions = state.borrow_mut::<PermissionsContainer>().0.lock();
+ // todo(dsherret): don't have this function use the properties of
+ // permission container
+ let permissions_container = state.borrow_mut::<PermissionsContainer>();
+ let desc_parser = &permissions_container.descriptor_parser;
+ let mut permissions = permissions_container.inner.lock();
let path = args.path.as_deref();
let perm = match args.name.as_ref() {
- "read" => permissions.read.request(path.map(Path::new)),
- "write" => permissions.write.request(path.map(Path::new)),
+ "read" => permissions.read.request(
+ path
+ .map(|path| {
+ Result::<_, AnyError>::Ok(
+ desc_parser.parse_path_query(path)?.into_read(),
+ )
+ })
+ .transpose()?
+ .as_ref(),
+ ),
+ "write" => permissions.write.request(
+ path
+ .map(|path| {
+ Result::<_, AnyError>::Ok(
+ desc_parser.parse_path_query(path)?.into_write(),
+ )
+ })
+ .transpose()?
+ .as_ref(),
+ ),
"net" => permissions.net.request(
match args.host.as_deref() {
None => None,
- Some(h) => Some(NetDescriptor::parse(h)?),
+ Some(h) => Some(desc_parser.parse_net_descriptor(h)?),
}
.as_ref(),
),
@@ -140,8 +244,24 @@ pub fn op_request_permission(
"sys" => permissions
.sys
.request(args.kind.as_deref().map(parse_sys_kind).transpose()?),
- "run" => permissions.run.request(args.command.as_deref()),
- "ffi" => permissions.ffi.request(args.path.as_deref().map(Path::new)),
+ "run" => permissions.run.request(
+ args
+ .command
+ .as_deref()
+ .map(|request| desc_parser.parse_run_query(request))
+ .transpose()?
+ .as_ref(),
+ ),
+ "ffi" => permissions.ffi.request(
+ path
+ .map(|path| {
+ Result::<_, AnyError>::Ok(
+ desc_parser.parse_path_query(path)?.into_ffi(),
+ )
+ })
+ .transpose()?
+ .as_ref(),
+ ),
n => {
return Err(custom_error(
"ReferenceError",
diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs
index b7242c07f..a39bb5f04 100644
--- a/runtime/ops/process.rs
+++ b/runtime/ops/process.rs
@@ -17,12 +17,13 @@ use deno_io::ChildStderrResource;
use deno_io::ChildStdinResource;
use deno_io::ChildStdoutResource;
use deno_permissions::PermissionsContainer;
-use deno_permissions::RunPathQuery;
+use deno_permissions::RunQueryDescriptor;
use serde::Deserialize;
use serde::Serialize;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
+use std::ffi::OsString;
use std::path::Path;
use std::path::PathBuf;
use std::process::ExitStatus;
@@ -536,9 +537,9 @@ fn compute_run_cmd_and_check_permissions(
.with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
check_run_permission(
state,
- RunPathQuery {
- requested: arg_cmd,
- resolved: &cmd,
+ &RunQueryDescriptor::Path {
+ requested: arg_cmd.to_string(),
+ resolved: cmd.clone(),
},
&run_env,
api_name,
@@ -547,7 +548,7 @@ fn compute_run_cmd_and_check_permissions(
}
struct RunEnv {
- envs: HashMap<String, String>,
+ envs: HashMap<OsString, OsString>,
cwd: PathBuf,
}
@@ -567,11 +568,32 @@ fn compute_run_env(
.map(|cwd_arg| resolve_path(cwd_arg, &cwd))
.unwrap_or(cwd);
let envs = if arg_clear_env {
- arg_envs.iter().cloned().collect()
+ arg_envs
+ .iter()
+ .map(|(k, v)| (OsString::from(k), OsString::from(v)))
+ .collect()
} else {
- let mut envs = std::env::vars().collect::<HashMap<_, _>>();
+ let mut envs = std::env::vars_os()
+ .map(|(k, v)| {
+ (
+ if cfg!(windows) {
+ k.to_ascii_uppercase()
+ } else {
+ k
+ },
+ v,
+ )
+ })
+ .collect::<HashMap<_, _>>();
for (key, value) in arg_envs {
- envs.insert(key.clone(), value.clone());
+ envs.insert(
+ OsString::from(if cfg!(windows) {
+ key.to_ascii_uppercase()
+ } else {
+ key.clone()
+ }),
+ OsString::from(value.clone()),
+ );
}
envs
};
@@ -585,19 +607,7 @@ fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, AnyError> {
if is_path {
Ok(resolve_path(cmd, &env.cwd))
} else {
- let path = env.envs.get("PATH").or_else(|| {
- if cfg!(windows) {
- env.envs.iter().find_map(|(k, v)| {
- if k.to_uppercase() == "PATH" {
- Some(v)
- } else {
- None
- }
- })
- } else {
- None
- }
- });
+ let path = env.envs.get(&OsString::from("PATH"));
match which::which_in(cmd, path, &env.cwd) {
Ok(cmd) => Ok(cmd),
Err(which::Error::CannotFindBinaryPath) => {
@@ -614,7 +624,7 @@ fn resolve_path(path: &str, cwd: &Path) -> PathBuf {
fn check_run_permission(
state: &mut OpState,
- cmd: RunPathQuery,
+ cmd: &RunQueryDescriptor,
run_env: &RunEnv,
api_name: &str,
) -> Result<(), AnyError> {
@@ -647,11 +657,22 @@ fn get_requires_allow_all_env_vars(env: &RunEnv) -> Vec<&str> {
key.starts_with("LD_") || key.starts_with("DYLD_")
}
+ fn is_empty(value: &OsString) -> bool {
+ value.is_empty()
+ || value.to_str().map(|v| v.trim().is_empty()).unwrap_or(false)
+ }
+
let mut found_envs = env
.envs
.iter()
- .filter(|(k, v)| requires_allow_all(k) && !v.trim().is_empty())
- .map(|(k, _)| k.as_str())
+ .filter_map(|(k, v)| {
+ let key = k.to_str()?;
+ if requires_allow_all(key) && !is_empty(v) {
+ Some(key)
+ } else {
+ None
+ }
+ })
.collect::<Vec<_>>();
found_envs.sort();
found_envs
diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs
index 19475fedf..3c0035645 100644
--- a/runtime/ops/worker_host.rs
+++ b/runtime/ops/worker_host.rs
@@ -19,6 +19,7 @@ use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_permissions::create_child_permissions;
use deno_permissions::ChildPermissionsArg;
+use deno_permissions::PermissionDescriptorParser;
use deno_permissions::PermissionsContainer;
use deno_web::deserialize_js_transferables;
use deno_web::JsMessageData;
@@ -153,13 +154,19 @@ fn op_create_worker(
"Worker.deno.permissions",
);
}
+ let permission_desc_parser = state
+ .borrow::<Arc<dyn PermissionDescriptorParser>>()
+ .clone();
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = if let Some(child_permissions_arg) = args.permissions
{
- let mut parent_permissions = parent_permissions.0.lock();
- let perms =
- create_child_permissions(&mut parent_permissions, child_permissions_arg)?;
- PermissionsContainer::new(perms)
+ let mut parent_permissions = parent_permissions.inner.lock();
+ let perms = create_child_permissions(
+ permission_desc_parser.as_ref(),
+ &mut parent_permissions,
+ child_permissions_arg,
+ )?;
+ PermissionsContainer::new(permission_desc_parser, perms)
} else {
parent_permissions.clone()
};
diff --git a/runtime/permissions.rs b/runtime/permissions.rs
new file mode 100644
index 000000000..a28ba35b1
--- /dev/null
+++ b/runtime/permissions.rs
@@ -0,0 +1,164 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::path::Path;
+use std::path::PathBuf;
+
+use deno_core::anyhow::bail;
+use deno_core::anyhow::Context;
+use deno_core::error::AnyError;
+use deno_core::normalize_path;
+use deno_permissions::AllowRunDescriptor;
+use deno_permissions::AllowRunDescriptorParseResult;
+use deno_permissions::DenyRunDescriptor;
+use deno_permissions::EnvDescriptor;
+use deno_permissions::FfiDescriptor;
+use deno_permissions::NetDescriptor;
+use deno_permissions::PathQueryDescriptor;
+use deno_permissions::ReadDescriptor;
+use deno_permissions::RunQueryDescriptor;
+use deno_permissions::SysDescriptor;
+use deno_permissions::WriteDescriptor;
+
+#[derive(Debug)]
+pub struct RuntimePermissionDescriptorParser {
+ fs: deno_fs::FileSystemRc,
+}
+
+impl RuntimePermissionDescriptorParser {
+ pub fn new(fs: deno_fs::FileSystemRc) -> Self {
+ Self { fs }
+ }
+
+ fn resolve_from_cwd(&self, path: &str) -> Result<PathBuf, AnyError> {
+ if path.is_empty() {
+ bail!("Empty path is not allowed");
+ }
+ let path = Path::new(path);
+ if path.is_absolute() {
+ Ok(normalize_path(path))
+ } else {
+ let cwd = self.resolve_cwd()?;
+ Ok(normalize_path(cwd.join(path)))
+ }
+ }
+
+ fn resolve_cwd(&self) -> Result<PathBuf, AnyError> {
+ self
+ .fs
+ .cwd()
+ .map_err(|e| e.into_io_error())
+ .context("failed resolving cwd")
+ }
+}
+
+impl deno_permissions::PermissionDescriptorParser
+ for RuntimePermissionDescriptorParser
+{
+ fn parse_read_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<ReadDescriptor, AnyError> {
+ Ok(ReadDescriptor(self.resolve_from_cwd(text)?))
+ }
+
+ fn parse_write_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<WriteDescriptor, AnyError> {
+ Ok(WriteDescriptor(self.resolve_from_cwd(text)?))
+ }
+
+ fn parse_net_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<NetDescriptor, AnyError> {
+ NetDescriptor::parse(text)
+ }
+
+ fn parse_env_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<EnvDescriptor, AnyError> {
+ if text.is_empty() {
+ Err(AnyError::msg("Empty env not allowed"))
+ } else {
+ Ok(EnvDescriptor::new(text))
+ }
+ }
+
+ fn parse_sys_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<deno_permissions::SysDescriptor, AnyError> {
+ if text.is_empty() {
+ Err(AnyError::msg("Empty sys not allowed"))
+ } else {
+ Ok(SysDescriptor(text.to_string()))
+ }
+ }
+
+ fn parse_allow_run_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<AllowRunDescriptorParseResult, AnyError> {
+ Ok(AllowRunDescriptor::parse(text, &self.resolve_cwd()?)?)
+ }
+
+ fn parse_deny_run_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<DenyRunDescriptor, AnyError> {
+ Ok(DenyRunDescriptor::parse(text, &self.resolve_cwd()?))
+ }
+
+ fn parse_ffi_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<deno_permissions::FfiDescriptor, AnyError> {
+ Ok(FfiDescriptor(self.resolve_from_cwd(text)?))
+ }
+
+ // queries
+
+ fn parse_path_query(
+ &self,
+ path: &str,
+ ) -> Result<PathQueryDescriptor, AnyError> {
+ Ok(PathQueryDescriptor {
+ resolved: self.resolve_from_cwd(path)?,
+ requested: path.to_string(),
+ })
+ }
+
+ fn parse_run_query(
+ &self,
+ requested: &str,
+ ) -> Result<RunQueryDescriptor, AnyError> {
+ if requested.is_empty() {
+ bail!("Empty run query is not allowed");
+ }
+ RunQueryDescriptor::parse(requested)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::sync::Arc;
+
+ use deno_fs::RealFs;
+ use deno_permissions::PermissionDescriptorParser;
+
+ use super::*;
+
+ #[test]
+ fn test_handle_empty_value() {
+ let parser = RuntimePermissionDescriptorParser::new(Arc::new(RealFs));
+ assert!(parser.parse_read_descriptor("").is_err());
+ assert!(parser.parse_write_descriptor("").is_err());
+ assert!(parser.parse_env_descriptor("").is_err());
+ assert!(parser.parse_net_descriptor("").is_err());
+ assert!(parser.parse_ffi_descriptor("").is_err());
+ assert!(parser.parse_path_query("").is_err());
+ assert!(parser.parse_run_query("").is_err());
+ }
+}
diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs
index 36750ae38..ad84b9fc3 100644
--- a/runtime/permissions/lib.rs
+++ b/runtime/permissions/lib.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::custom_error;
use deno_core::error::type_error;
@@ -51,14 +52,11 @@ macro_rules! skip_check_if_is_permission_fully_granted {
}
#[inline]
-fn resolve_from_cwd(path: &Path) -> Result<PathBuf, AnyError> {
+fn resolve_from_known_cwd(path: &Path, cwd: &Path) -> PathBuf {
if path.is_absolute() {
- Ok(normalize_path(path))
+ normalize_path(path)
} else {
- #[allow(clippy::disallowed_methods)]
- let cwd = std::env::current_dir()
- .context("Failed to get current working directory")?;
- Ok(normalize_path(cwd.join(path)))
+ normalize_path(cwd.join(path))
}
}
@@ -310,12 +308,17 @@ impl AsRef<str> for EnvVarName {
}
}
-pub trait Descriptor: Eq + Clone + Hash {
- type Arg: From<String>;
+pub trait QueryDescriptor: Debug {
+ type AllowDesc: Debug + Eq + Clone + Hash;
+ type DenyDesc: Debug + Eq + Clone + Hash;
+
+ fn flag_name() -> &'static str;
+ fn display_name(&self) -> Cow<str>;
+
+ fn from_allow(allow: &Self::AllowDesc) -> Self;
- /// Parse this descriptor from a list of Self::Arg, which may have been converted from
- /// command-line strings.
- fn parse(list: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError>;
+ fn as_allow(&self) -> Option<Self::AllowDesc>;
+ fn as_deny(&self) -> Self::DenyDesc;
/// Generic check function to check this descriptor against a `UnaryPermission`.
fn check_in_permission(
@@ -324,27 +327,35 @@ pub trait Descriptor: Eq + Clone + Hash {
api_name: Option<&str>,
) -> Result<(), AnyError>;
- fn flag_name() -> &'static str;
- fn name(&self) -> Cow<str>;
- // By default, specifies no-stronger-than relationship.
- // As this is not strict, it's only true when descriptors are the same.
- fn stronger_than(&self, other: &Self) -> bool {
- self == other
+ fn matches_allow(&self, other: &Self::AllowDesc) -> bool;
+ fn matches_deny(&self, other: &Self::DenyDesc) -> bool;
+
+ /// Gets if this query descriptor should revoke the provided allow descriptor.
+ fn revokes(&self, other: &Self::AllowDesc) -> bool;
+ fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool;
+ fn overlaps_deny(&self, other: &Self::DenyDesc) -> bool;
+}
+
+fn format_display_name(display_name: Cow<str>) -> String {
+ if display_name.starts_with('<') && display_name.ends_with('>') {
+ display_name.into_owned()
+ } else {
+ format!("\"{}\"", display_name)
}
}
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct UnaryPermission<T: Descriptor + Hash> {
+#[derive(Debug, Eq, PartialEq)]
+pub struct UnaryPermission<TQuery: QueryDescriptor + ?Sized> {
granted_global: bool,
- granted_list: HashSet<T>,
+ granted_list: HashSet<TQuery::AllowDesc>,
flag_denied_global: bool,
- flag_denied_list: HashSet<T>,
+ flag_denied_list: HashSet<TQuery::DenyDesc>,
prompt_denied_global: bool,
- prompt_denied_list: HashSet<T>,
+ prompt_denied_list: HashSet<TQuery::DenyDesc>,
prompt: bool,
}
-impl<T: Descriptor + Hash> Default for UnaryPermission<T> {
+impl<TQuery: QueryDescriptor> Default for UnaryPermission<TQuery> {
fn default() -> Self {
UnaryPermission {
granted_global: Default::default(),
@@ -358,7 +369,21 @@ impl<T: Descriptor + Hash> Default for UnaryPermission<T> {
}
}
-impl<T: Descriptor + Hash> UnaryPermission<T> {
+impl<TQuery: QueryDescriptor> Clone for UnaryPermission<TQuery> {
+ fn clone(&self) -> Self {
+ Self {
+ granted_global: self.granted_global,
+ granted_list: self.granted_list.clone(),
+ flag_denied_global: self.flag_denied_global,
+ flag_denied_list: self.flag_denied_list.clone(),
+ prompt_denied_global: self.prompt_denied_global,
+ prompt_denied_list: self.prompt_denied_list.clone(),
+ prompt: self.prompt,
+ }
+ }
+}
+
+impl<TQuery: QueryDescriptor> UnaryPermission<TQuery> {
pub fn allow_all() -> Self {
Self {
granted_global: true,
@@ -377,26 +402,21 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(None, false, api_name, || None)
+ self.check_desc(None, false, api_name)
}
fn check_desc(
&mut self,
- desc: Option<&T>,
+ desc: Option<&TQuery>,
assert_non_partial: bool,
api_name: Option<&str>,
- get_display_name: impl Fn() -> Option<String>,
) -> Result<(), AnyError> {
- skip_check_if_is_permission_fully_granted!(self);
let (result, prompted, is_allow_all) = self
.query_desc(desc, AllowPartial::from(!assert_non_partial))
.check2(
- T::flag_name(),
+ TQuery::flag_name(),
api_name,
- || match get_display_name() {
- Some(display_name) => Some(display_name),
- None => desc.map(|d| format!("\"{}\"", d.name())),
- },
+ || desc.map(|d| format_display_name(d.display_name())),
self.prompt,
);
if prompted {
@@ -404,10 +424,10 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
if is_allow_all {
self.insert_granted(None);
} else {
- self.insert_granted(desc.cloned());
+ self.insert_granted(desc);
}
} else {
- self.insert_prompt_denied(desc.cloned());
+ self.insert_prompt_denied(desc.map(|d| d.as_deny()));
}
}
result
@@ -415,7 +435,7 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
fn query_desc(
&self,
- desc: Option<&T>,
+ desc: Option<&TQuery>,
allow_partial: AllowPartial,
) -> PermissionState {
if self.is_flag_denied(desc) || self.is_prompt_denied(desc) {
@@ -447,43 +467,33 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
}
}
- fn request_desc(
- &mut self,
- desc: Option<&T>,
- get_display_name: impl Fn() -> Option<String>,
- ) -> PermissionState {
+ fn request_desc(&mut self, desc: Option<&TQuery>) -> PermissionState {
let state = self.query_desc(desc, AllowPartial::TreatAsPartialGranted);
if state == PermissionState::Granted {
- self.insert_granted(desc.cloned());
+ self.insert_granted(desc);
return state;
}
if state != PermissionState::Prompt {
return state;
}
let mut message = String::with_capacity(40);
- message.push_str(&format!("{} access", T::flag_name()));
- match get_display_name() {
- Some(display_name) => {
- message.push_str(&format!(" to \"{}\"", display_name))
- }
- None => {
- if let Some(desc) = desc {
- message.push_str(&format!(" to \"{}\"", desc.name()));
- }
- }
+ message.push_str(&format!("{} access", TQuery::flag_name()));
+ if let Some(desc) = desc {
+ message
+ .push_str(&format!(" to {}", format_display_name(desc.display_name())));
}
match permission_prompt(
&message,
- T::flag_name(),
+ TQuery::flag_name(),
Some("Deno.permissions.request()"),
true,
) {
PromptResponse::Allow => {
- self.insert_granted(desc.cloned());
+ self.insert_granted(desc);
PermissionState::Granted
}
PromptResponse::Deny => {
- self.insert_prompt_denied(desc.cloned());
+ self.insert_prompt_denied(desc.map(|d| d.as_deny()));
PermissionState::Denied
}
PromptResponse::AllowAll => {
@@ -493,10 +503,10 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
}
}
- fn revoke_desc(&mut self, desc: Option<&T>) -> PermissionState {
+ fn revoke_desc(&mut self, desc: Option<&TQuery>) -> PermissionState {
match desc {
Some(desc) => {
- self.granted_list.retain(|v| !v.stronger_than(desc));
+ self.granted_list.retain(|v| !desc.revokes(v));
}
None => {
self.granted_global = false;
@@ -509,47 +519,61 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
self.query_desc(desc, AllowPartial::TreatAsPartialGranted)
}
- fn is_granted(&self, desc: Option<&T>) -> bool {
- Self::list_contains(desc, self.granted_global, &self.granted_list)
+ fn is_granted(&self, query: Option<&TQuery>) -> bool {
+ match query {
+ Some(query) => {
+ self.granted_global
+ || self.granted_list.iter().any(|v| query.matches_allow(v))
+ }
+ None => self.granted_global,
+ }
}
- fn is_flag_denied(&self, desc: Option<&T>) -> bool {
- Self::list_contains(desc, self.flag_denied_global, &self.flag_denied_list)
+ fn is_flag_denied(&self, query: Option<&TQuery>) -> bool {
+ match query {
+ Some(query) => {
+ self.flag_denied_global
+ || self.flag_denied_list.iter().any(|v| query.matches_deny(v))
+ }
+ None => self.flag_denied_global,
+ }
}
- fn is_prompt_denied(&self, desc: Option<&T>) -> bool {
- match desc {
- Some(desc) => self
+ fn is_prompt_denied(&self, query: Option<&TQuery>) -> bool {
+ match query {
+ Some(query) => self
.prompt_denied_list
.iter()
- .any(|v| desc.stronger_than(v)),
+ .any(|v| query.stronger_than_deny(v)),
None => self.prompt_denied_global || !self.prompt_denied_list.is_empty(),
}
}
- fn is_partial_flag_denied(&self, desc: Option<&T>) -> bool {
- match desc {
+ fn is_partial_flag_denied(&self, query: Option<&TQuery>) -> bool {
+ match query {
None => !self.flag_denied_list.is_empty(),
- Some(desc) => self.flag_denied_list.iter().any(|v| desc.stronger_than(v)),
- }
- }
-
- fn list_contains(
- desc: Option<&T>,
- list_global: bool,
- list: &HashSet<T>,
- ) -> bool {
- match desc {
- Some(desc) => list_global || list.iter().any(|v| v.stronger_than(desc)),
- None => list_global,
+ Some(query) => {
+ self.flag_denied_list.iter().any(|v| query.overlaps_deny(v))
+ }
}
}
- fn insert_granted(&mut self, desc: Option<T>) {
+ fn insert_granted(&mut self, query: Option<&TQuery>) -> bool {
+ let desc = match query.map(|q| q.as_allow()) {
+ Some(Some(allow_desc)) => Some(allow_desc),
+ Some(None) => {
+ // the user was prompted for this descriptor in order to not
+ // expose anything about the system to the program, but the
+ // descriptor wasn't valid so no permission was raised
+ return false;
+ }
+ None => None,
+ };
Self::list_insert(desc, &mut self.granted_global, &mut self.granted_list);
+ true
}
- fn insert_prompt_denied(&mut self, desc: Option<T>) {
+ fn insert_prompt_denied(&mut self, desc: Option<TQuery::DenyDesc>) {
Self::list_insert(
desc,
&mut self.prompt_denied_global,
@@ -557,7 +581,7 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
);
}
- fn list_insert(
+ fn list_insert<T: Hash + Eq>(
desc: Option<T>,
list_global: &mut bool,
list: &mut HashSet<T>,
@@ -573,7 +597,8 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
fn create_child_permissions(
&mut self,
flag: ChildUnaryPermissionArg,
- ) -> Result<UnaryPermission<T>, AnyError> {
+ parse: impl Fn(&str) -> Result<Option<TQuery::AllowDesc>, AnyError>,
+ ) -> Result<UnaryPermission<TQuery>, AnyError> {
let mut perms = Self::default();
match flag {
@@ -588,14 +613,15 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
}
ChildUnaryPermissionArg::NotGranted => {}
ChildUnaryPermissionArg::GrantedList(granted_list) => {
- let granted: Vec<T::Arg> =
- granted_list.into_iter().map(From::from).collect();
- perms.granted_list = T::parse(Some(&granted))?;
- if !perms
- .granted_list
+ perms.granted_list = granted_list
.iter()
- .all(|desc| desc.check_in_permission(self, None).is_ok())
- {
+ .filter_map(|i| parse(i).transpose())
+ .collect::<Result<_, _>>()?;
+ if !perms.granted_list.iter().all(|desc| {
+ TQuery::from_allow(desc)
+ .check_in_permission(self, None)
+ .is_ok()
+ }) {
return Err(escalation_error());
}
}
@@ -613,10 +639,55 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
-pub struct ReadDescriptor(pub PathBuf);
+pub struct PathQueryDescriptor {
+ pub requested: String,
+ pub resolved: PathBuf,
+}
-impl Descriptor for ReadDescriptor {
- type Arg = PathBuf;
+impl PathQueryDescriptor {
+ pub fn into_ffi(self) -> FfiQueryDescriptor {
+ FfiQueryDescriptor(self)
+ }
+
+ pub fn into_read(self) -> ReadQueryDescriptor {
+ ReadQueryDescriptor(self)
+ }
+
+ pub fn into_write(self) -> WriteQueryDescriptor {
+ WriteQueryDescriptor(self)
+ }
+}
+
+#[derive(Clone, Eq, PartialEq, Hash, Debug)]
+pub struct ReadQueryDescriptor(pub PathQueryDescriptor);
+
+impl QueryDescriptor for ReadQueryDescriptor {
+ type AllowDesc = ReadDescriptor;
+ type DenyDesc = ReadDescriptor;
+
+ fn flag_name() -> &'static str {
+ "read"
+ }
+
+ fn display_name(&self) -> Cow<str> {
+ Cow::Borrowed(self.0.requested.as_str())
+ }
+
+ fn from_allow(allow: &Self::AllowDesc) -> Self {
+ PathQueryDescriptor {
+ requested: allow.0.to_string_lossy().into_owned(),
+ resolved: allow.0.clone(),
+ }
+ .into_read()
+ }
+
+ fn as_allow(&self) -> Option<Self::AllowDesc> {
+ Some(ReadDescriptor(self.0.resolved.clone()))
+ }
+
+ fn as_deny(&self) -> Self::DenyDesc {
+ ReadDescriptor(self.0.resolved.clone())
+ }
fn check_in_permission(
&self,
@@ -624,31 +695,62 @@ impl Descriptor for ReadDescriptor {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(perm);
- perm.check_desc(Some(self), true, api_name, || None)
+ perm.check_desc(Some(self), true, api_name)
}
- fn parse(args: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
- parse_path_list(args, ReadDescriptor)
+ fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
+ self.0.resolved.starts_with(&other.0)
}
- fn flag_name() -> &'static str {
- "read"
+ fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
+ self.0.resolved.starts_with(&other.0)
}
- fn name(&self) -> Cow<str> {
- Cow::from(self.0.display().to_string())
+ fn revokes(&self, other: &Self::AllowDesc) -> bool {
+ self.matches_allow(other)
}
- fn stronger_than(&self, other: &Self) -> bool {
- other.0.starts_with(&self.0)
+ fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
+ other.0.starts_with(&self.0.resolved)
+ }
+
+ fn overlaps_deny(&self, other: &Self::DenyDesc) -> bool {
+ self.stronger_than_deny(other)
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
-pub struct WriteDescriptor(pub PathBuf);
+pub struct ReadDescriptor(pub PathBuf);
+
+#[derive(Clone, Eq, PartialEq, Hash, Debug)]
+pub struct WriteQueryDescriptor(pub PathQueryDescriptor);
-impl Descriptor for WriteDescriptor {
- type Arg = PathBuf;
+impl QueryDescriptor for WriteQueryDescriptor {
+ type AllowDesc = WriteDescriptor;
+ type DenyDesc = WriteDescriptor;
+
+ fn flag_name() -> &'static str {
+ "write"
+ }
+
+ fn display_name(&self) -> Cow<str> {
+ Cow::Borrowed(&self.0.requested)
+ }
+
+ fn from_allow(allow: &Self::AllowDesc) -> Self {
+ WriteQueryDescriptor(PathQueryDescriptor {
+ requested: allow.0.to_string_lossy().into_owned(),
+ resolved: allow.0.clone(),
+ })
+ }
+
+ fn as_allow(&self) -> Option<Self::AllowDesc> {
+ Some(WriteDescriptor(self.0.resolved.clone()))
+ }
+
+ fn as_deny(&self) -> Self::DenyDesc {
+ WriteDescriptor(self.0.resolved.clone())
+ }
fn check_in_permission(
&self,
@@ -656,27 +758,34 @@ impl Descriptor for WriteDescriptor {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(perm);
- perm.check_desc(Some(self), true, api_name, || None)
+ perm.check_desc(Some(self), true, api_name)
}
- fn parse(args: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
- parse_path_list(args, WriteDescriptor)
+ fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
+ self.0.resolved.starts_with(&other.0)
}
- fn flag_name() -> &'static str {
- "write"
+ fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
+ self.0.resolved.starts_with(&other.0)
}
- fn name(&self) -> Cow<str> {
- Cow::from(self.0.display().to_string())
+ fn revokes(&self, other: &Self::AllowDesc) -> bool {
+ self.matches_allow(other)
}
- fn stronger_than(&self, other: &Self) -> bool {
- other.0.starts_with(&self.0)
+ fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
+ other.0.starts_with(&self.0.resolved)
+ }
+
+ fn overlaps_deny(&self, other: &Self::DenyDesc) -> bool {
+ self.stronger_than_deny(other)
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
+pub struct WriteDescriptor(pub PathBuf);
+
+#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub enum Host {
Fqdn(FQDN),
Ip(IpAddr),
@@ -728,8 +837,29 @@ impl Host {
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct NetDescriptor(pub Host, pub Option<u16>);
-impl Descriptor for NetDescriptor {
- type Arg = String;
+impl QueryDescriptor for NetDescriptor {
+ type AllowDesc = NetDescriptor;
+ type DenyDesc = NetDescriptor;
+
+ fn flag_name() -> &'static str {
+ "net"
+ }
+
+ fn display_name(&self) -> Cow<str> {
+ Cow::from(format!("{}", self))
+ }
+
+ fn from_allow(allow: &Self::AllowDesc) -> Self {
+ allow.clone()
+ }
+
+ fn as_allow(&self) -> Option<Self::AllowDesc> {
+ Some(self.clone())
+ }
+
+ fn as_deny(&self) -> Self::DenyDesc {
+ self.clone()
+ }
fn check_in_permission(
&self,
@@ -737,23 +867,27 @@ impl Descriptor for NetDescriptor {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(perm);
- perm.check_desc(Some(self), false, api_name, || None)
+ perm.check_desc(Some(self), false, api_name)
}
- fn parse(args: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
- parse_net_list(args)
+ fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
+ self.0 == other.0 && (other.1.is_none() || self.1 == other.1)
}
- fn flag_name() -> &'static str {
- "net"
+ fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
+ self.0 == other.0 && (other.1.is_none() || self.1 == other.1)
}
- fn name(&self) -> Cow<str> {
- Cow::from(format!("{}", self))
+ fn revokes(&self, other: &Self::AllowDesc) -> bool {
+ self.matches_allow(other)
+ }
+
+ fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
+ self.matches_deny(other)
}
- fn stronger_than(&self, other: &Self) -> bool {
- self.0 == other.0 && (self.1.is_none() || self.1 == other.1)
+ fn overlaps_deny(&self, _other: &Self::DenyDesc) -> bool {
+ false
}
}
@@ -837,8 +971,29 @@ impl EnvDescriptor {
}
}
-impl Descriptor for EnvDescriptor {
- type Arg = String;
+impl QueryDescriptor for EnvDescriptor {
+ type AllowDesc = EnvDescriptor;
+ type DenyDesc = EnvDescriptor;
+
+ fn flag_name() -> &'static str {
+ "env"
+ }
+
+ fn display_name(&self) -> Cow<str> {
+ Cow::from(self.0.as_ref())
+ }
+
+ fn from_allow(allow: &Self::AllowDesc) -> Self {
+ allow.clone()
+ }
+
+ fn as_allow(&self) -> Option<Self::AllowDesc> {
+ Some(self.clone())
+ }
+
+ fn as_deny(&self) -> Self::DenyDesc {
+ self.clone()
+ }
fn check_in_permission(
&self,
@@ -846,19 +1001,27 @@ impl Descriptor for EnvDescriptor {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(perm);
- perm.check_desc(Some(self), false, api_name, || None)
+ perm.check_desc(Some(self), false, api_name)
}
- fn parse(list: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
- parse_env_list(list)
+ fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
+ self == other
}
- fn flag_name() -> &'static str {
- "env"
+ fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
+ self == other
}
- fn name(&self) -> Cow<str> {
- Cow::from(self.0.as_ref())
+ fn revokes(&self, other: &Self::AllowDesc) -> bool {
+ self == other
+ }
+
+ fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
+ self == other
+ }
+
+ fn overlaps_deny(&self, _other: &Self::DenyDesc) -> bool {
+ false
}
}
@@ -869,46 +1032,94 @@ impl AsRef<str> for EnvDescriptor {
}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
-pub struct RunPathQuery<'a> {
- pub requested: &'a str,
- pub resolved: &'a Path,
-}
-
-pub enum RunDescriptorArg {
- Name(String),
- Path(PathBuf),
-}
-
-#[derive(Clone, Eq, PartialEq, Hash, Debug)]
-pub enum RunDescriptor {
- /// Warning: You may want to construct with `RunDescriptor::from()` for case
- /// handling.
+pub enum RunQueryDescriptor {
+ Path {
+ requested: String,
+ resolved: PathBuf,
+ },
+ /// This variant won't actually grant permissions because the path of
+ /// the executable is unresolved. It's mostly used so that prompts and
+ /// everything works the same way as when the command is resolved,
+ /// meaning that a script can't tell
+ /// if a command is resolved or not based on how long something
+ /// takes to ask for permissions.
Name(String),
- /// Warning: You may want to construct with `RunDescriptor::from()` for case
- /// handling.
- Path(PathBuf),
}
-impl From<String> for RunDescriptorArg {
- fn from(s: String) -> Self {
- #[cfg(windows)]
- let s = s.to_lowercase();
- let is_path = s.contains('/');
- #[cfg(windows)]
- let is_path = is_path || s.contains('\\') || Path::new(&s).is_absolute();
- if is_path {
- Self::Path(resolve_from_cwd(Path::new(&s)).unwrap())
+impl RunQueryDescriptor {
+ pub fn parse(requested: &str) -> Result<RunQueryDescriptor, AnyError> {
+ if is_path(requested) {
+ let path = PathBuf::from(requested);
+ let resolved = if path.is_absolute() {
+ normalize_path(path)
+ } else {
+ let cwd = std::env::current_dir().context("failed resolving cwd")?;
+ normalize_path(cwd.join(path))
+ };
+ Ok(RunQueryDescriptor::Path {
+ requested: requested.to_string(),
+ resolved,
+ })
} else {
- match which::which(&s) {
- Ok(path) => Self::Path(path),
- Err(_) => Self::Name(s),
+ match which::which(requested) {
+ Ok(resolved) => Ok(RunQueryDescriptor::Path {
+ requested: requested.to_string(),
+ resolved,
+ }),
+ Err(_) => Ok(RunQueryDescriptor::Name(requested.to_string())),
}
}
}
}
-impl Descriptor for RunDescriptor {
- type Arg = RunDescriptorArg;
+impl QueryDescriptor for RunQueryDescriptor {
+ type AllowDesc = AllowRunDescriptor;
+ type DenyDesc = DenyRunDescriptor;
+
+ fn flag_name() -> &'static str {
+ "run"
+ }
+
+ fn display_name(&self) -> Cow<str> {
+ match self {
+ RunQueryDescriptor::Path { requested, .. } => Cow::Borrowed(requested),
+ RunQueryDescriptor::Name(name) => Cow::Borrowed(name),
+ }
+ }
+
+ fn from_allow(allow: &Self::AllowDesc) -> Self {
+ RunQueryDescriptor::Path {
+ requested: allow.0.to_string_lossy().into_owned(),
+ resolved: allow.0.clone(),
+ }
+ }
+
+ fn as_allow(&self) -> Option<Self::AllowDesc> {
+ match self {
+ RunQueryDescriptor::Path { resolved, .. } => {
+ Some(AllowRunDescriptor(resolved.clone()))
+ }
+ RunQueryDescriptor::Name(_) => None,
+ }
+ }
+
+ fn as_deny(&self) -> Self::DenyDesc {
+ match self {
+ RunQueryDescriptor::Path {
+ resolved,
+ requested,
+ } => {
+ if requested.contains('/')
+ || (cfg!(windows) && requested.contains("\\"))
+ {
+ DenyRunDescriptor::Path(resolved.clone())
+ } else {
+ DenyRunDescriptor::Name(requested.clone())
+ }
+ }
+ RunQueryDescriptor::Name(name) => DenyRunDescriptor::Name(name.clone()),
+ }
+ }
fn check_in_permission(
&self,
@@ -916,71 +1127,180 @@ impl Descriptor for RunDescriptor {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(perm);
- perm.check_desc(Some(self), false, api_name, || None)
+ perm.check_desc(Some(self), false, api_name)
}
- fn parse(args: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
- parse_run_list(args)
+ fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
+ match self {
+ RunQueryDescriptor::Path { resolved, .. } => *resolved == other.0,
+ RunQueryDescriptor::Name(_) => false,
+ }
}
- fn flag_name() -> &'static str {
- "run"
+ fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
+ match other {
+ DenyRunDescriptor::Name(deny_desc) => match self {
+ RunQueryDescriptor::Path { resolved, .. } => {
+ denies_run_name(deny_desc, resolved)
+ }
+ RunQueryDescriptor::Name(query) => query == deny_desc,
+ },
+ DenyRunDescriptor::Path(deny_desc) => match self {
+ RunQueryDescriptor::Path { resolved, .. } => {
+ resolved.starts_with(deny_desc)
+ }
+ RunQueryDescriptor::Name(query) => denies_run_name(query, deny_desc),
+ },
+ }
+ }
+
+ fn revokes(&self, other: &Self::AllowDesc) -> bool {
+ match self {
+ RunQueryDescriptor::Path {
+ resolved,
+ requested,
+ } => {
+ if *resolved == other.0 {
+ return true;
+ }
+ if is_path(requested) {
+ false
+ } else {
+ denies_run_name(requested, &other.0)
+ }
+ }
+ RunQueryDescriptor::Name(query) => denies_run_name(query, &other.0),
+ }
}
- fn name(&self) -> Cow<str> {
- Cow::from(self.to_string())
+ fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
+ self.matches_deny(other)
}
+
+ fn overlaps_deny(&self, _other: &Self::DenyDesc) -> bool {
+ false
+ }
+}
+
+pub enum RunDescriptorArg {
+ Name(String),
+ Path(PathBuf),
+}
+
+pub enum AllowRunDescriptorParseResult {
+ /// An error occured getting the descriptor that should
+ /// be surfaced as a warning when launching deno, but should
+ /// be ignored when creating a worker.
+ Unresolved(Box<which::Error>),
+ Descriptor(AllowRunDescriptor),
}
-impl From<String> for RunDescriptor {
- fn from(s: String) -> Self {
- #[cfg(windows)]
- let s = s.to_lowercase();
- let is_path = s.contains('/');
- #[cfg(windows)]
- let is_path = is_path || s.contains('\\') || Path::new(&s).is_absolute();
- if is_path {
- Self::Path(resolve_from_cwd(Path::new(&s)).unwrap())
+#[derive(Debug, Clone, Hash, Eq, PartialEq)]
+pub struct AllowRunDescriptor(pub PathBuf);
+
+impl AllowRunDescriptor {
+ pub fn parse(
+ text: &str,
+ cwd: &Path,
+ ) -> Result<AllowRunDescriptorParseResult, which::Error> {
+ let is_path = is_path(text);
+ // todo(dsherret): canonicalize in #25458
+ let path = if is_path {
+ resolve_from_known_cwd(Path::new(text), cwd)
} else {
- match which::which(&s) {
- Ok(path) => Self::Path(path),
- Err(_) => Self::Name(s),
+ match which::which_in(text, std::env::var_os("PATH"), cwd) {
+ Ok(path) => path,
+ Err(err) => match err {
+ which::Error::BadAbsolutePath | which::Error::BadRelativePath => {
+ return Err(err);
+ }
+ which::Error::CannotFindBinaryPath
+ | which::Error::CannotGetCurrentDir
+ | which::Error::CannotCanonicalize => {
+ return Ok(AllowRunDescriptorParseResult::Unresolved(Box::new(err)))
+ }
+ },
}
- }
+ };
+ Ok(AllowRunDescriptorParseResult::Descriptor(
+ AllowRunDescriptor(path),
+ ))
}
}
-impl From<PathBuf> for RunDescriptor {
- fn from(p: PathBuf) -> Self {
- #[cfg(windows)]
- let p = PathBuf::from(p.to_string_lossy().to_string().to_lowercase());
- Self::Path(resolve_from_cwd(&p).unwrap())
- }
+#[derive(Clone, Eq, PartialEq, Hash, Debug)]
+pub enum DenyRunDescriptor {
+ /// Warning: You may want to construct with `RunDescriptor::from()` for case
+ /// handling.
+ Name(String),
+ /// Warning: You may want to construct with `RunDescriptor::from()` for case
+ /// handling.
+ Path(PathBuf),
}
-impl std::fmt::Display for RunDescriptor {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- RunDescriptor::Name(s) => f.write_str(s),
- RunDescriptor::Path(p) => f.write_str(&p.display().to_string()),
+impl DenyRunDescriptor {
+ pub fn parse(text: &str, cwd: &Path) -> Self {
+ if text.contains('/') || cfg!(windows) && text.contains('\\') {
+ let path = resolve_from_known_cwd(Path::new(&text), cwd);
+ DenyRunDescriptor::Path(path)
+ } else {
+ DenyRunDescriptor::Name(text.to_string())
}
}
}
-impl AsRef<Path> for RunDescriptor {
- fn as_ref(&self) -> &Path {
- match self {
- RunDescriptor::Name(s) => s.as_ref(),
- RunDescriptor::Path(s) => s.as_ref(),
- }
+fn is_path(text: &str) -> bool {
+ if cfg!(windows) {
+ text.contains('/') || text.contains('\\') || Path::new(text).is_absolute()
+ } else {
+ text.contains('/')
+ }
+}
+
+fn denies_run_name(name: &str, cmd_path: &Path) -> bool {
+ let Some(file_stem) = cmd_path.file_stem() else {
+ return false;
+ };
+ let Some(file_stem) = file_stem.to_str() else {
+ return false;
+ };
+ if file_stem.len() < name.len() {
+ return false;
}
+ let (prefix, suffix) = file_stem.split_at(name.len());
+ if !prefix.eq_ignore_ascii_case(name) {
+ return false;
+ }
+ // be broad and consider anything like `deno.something` as matching deny perms
+ suffix.is_empty() || suffix.starts_with('.')
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct SysDescriptor(pub String);
-impl Descriptor for SysDescriptor {
- type Arg = String;
+impl QueryDescriptor for SysDescriptor {
+ type AllowDesc = SysDescriptor;
+ type DenyDesc = SysDescriptor;
+
+ fn flag_name() -> &'static str {
+ "sys"
+ }
+
+ fn display_name(&self) -> Cow<str> {
+ Cow::from(self.0.to_string())
+ }
+
+ fn from_allow(allow: &Self::AllowDesc) -> Self {
+ allow.clone()
+ }
+
+ fn as_allow(&self) -> Option<Self::AllowDesc> {
+ Some(self.clone())
+ }
+
+ fn as_deny(&self) -> Self::DenyDesc {
+ self.clone()
+ }
fn check_in_permission(
&self,
@@ -988,19 +1308,27 @@ impl Descriptor for SysDescriptor {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(perm);
- perm.check_desc(Some(self), false, api_name, || None)
+ perm.check_desc(Some(self), false, api_name)
}
- fn parse(list: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
- parse_sys_list(list)
+ fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
+ self == other
}
- fn flag_name() -> &'static str {
- "sys"
+ fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
+ self == other
}
- fn name(&self) -> Cow<str> {
- Cow::from(self.0.to_string())
+ fn revokes(&self, other: &Self::AllowDesc) -> bool {
+ self == other
+ }
+
+ fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
+ self == other
+ }
+
+ fn overlaps_deny(&self, _other: &Self::DenyDesc) -> bool {
+ false
}
}
@@ -1014,10 +1342,35 @@ pub fn parse_sys_kind(kind: &str) -> Result<&str, AnyError> {
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
-pub struct FfiDescriptor(pub PathBuf);
+pub struct FfiQueryDescriptor(pub PathQueryDescriptor);
+
+impl QueryDescriptor for FfiQueryDescriptor {
+ type AllowDesc = FfiDescriptor;
+ type DenyDesc = FfiDescriptor;
+
+ fn flag_name() -> &'static str {
+ "ffi"
+ }
+
+ fn display_name(&self) -> Cow<str> {
+ Cow::Borrowed(&self.0.requested)
+ }
-impl Descriptor for FfiDescriptor {
- type Arg = PathBuf;
+ fn from_allow(allow: &Self::AllowDesc) -> Self {
+ PathQueryDescriptor {
+ requested: allow.0.to_string_lossy().into_owned(),
+ resolved: allow.0.clone(),
+ }
+ .into_ffi()
+ }
+
+ fn as_allow(&self) -> Option<Self::AllowDesc> {
+ Some(FfiDescriptor(self.0.resolved.clone()))
+ }
+
+ fn as_deny(&self) -> Self::DenyDesc {
+ FfiDescriptor(self.0.resolved.clone())
+ }
fn check_in_permission(
&self,
@@ -1025,175 +1378,118 @@ impl Descriptor for FfiDescriptor {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(perm);
- perm.check_desc(Some(self), true, api_name, || None)
+ perm.check_desc(Some(self), true, api_name)
}
- fn parse(list: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
- parse_path_list(list, FfiDescriptor)
+ fn matches_allow(&self, other: &Self::AllowDesc) -> bool {
+ self.0.resolved.starts_with(&other.0)
}
- fn flag_name() -> &'static str {
- "ffi"
+ fn matches_deny(&self, other: &Self::DenyDesc) -> bool {
+ self.0.resolved.starts_with(&other.0)
+ }
+
+ fn revokes(&self, other: &Self::AllowDesc) -> bool {
+ self.matches_allow(other)
}
- fn name(&self) -> Cow<str> {
- Cow::from(self.0.display().to_string())
+ fn stronger_than_deny(&self, other: &Self::DenyDesc) -> bool {
+ other.0.starts_with(&self.0.resolved)
}
- fn stronger_than(&self, other: &Self) -> bool {
- other.0.starts_with(&self.0)
+ fn overlaps_deny(&self, other: &Self::DenyDesc) -> bool {
+ self.stronger_than_deny(other)
}
}
-impl UnaryPermission<ReadDescriptor> {
- pub fn query(&self, path: Option<&Path>) -> PermissionState {
- self.query_desc(
- path
- .map(|p| ReadDescriptor(resolve_from_cwd(p).unwrap()))
- .as_ref(),
- AllowPartial::TreatAsPartialGranted,
- )
+#[derive(Clone, Eq, PartialEq, Hash, Debug)]
+pub struct FfiDescriptor(pub PathBuf);
+
+impl UnaryPermission<ReadQueryDescriptor> {
+ pub fn query(&self, desc: Option<&ReadQueryDescriptor>) -> PermissionState {
+ self.query_desc(desc, AllowPartial::TreatAsPartialGranted)
}
- pub fn request(&mut self, path: Option<&Path>) -> PermissionState {
- self.request_desc(
- path
- .map(|p| ReadDescriptor(resolve_from_cwd(p).unwrap()))
- .as_ref(),
- || Some(path?.display().to_string()),
- )
+ pub fn request(
+ &mut self,
+ path: Option<&ReadQueryDescriptor>,
+ ) -> PermissionState {
+ self.request_desc(path)
}
- pub fn revoke(&mut self, path: Option<&Path>) -> PermissionState {
- self.revoke_desc(
- path
- .map(|p| ReadDescriptor(resolve_from_cwd(p).unwrap()))
- .as_ref(),
- )
+ pub fn revoke(
+ &mut self,
+ desc: Option<&ReadQueryDescriptor>,
+ ) -> PermissionState {
+ self.revoke_desc(desc)
}
pub fn check(
&mut self,
- path: &Path,
+ desc: &ReadQueryDescriptor,
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(
- Some(&ReadDescriptor(resolve_from_cwd(path)?)),
- true,
- api_name,
- || Some(format!("\"{}\"", path.display())),
- )
+ self.check_desc(Some(desc), true, api_name)
}
#[inline]
pub fn check_partial(
&mut self,
- path: &Path,
+ desc: &ReadQueryDescriptor,
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- let desc = ReadDescriptor(resolve_from_cwd(path)?);
- self.check_desc(Some(&desc), false, api_name, || {
- Some(format!("\"{}\"", path.display()))
- })
- }
-
- /// As `check()`, but permission error messages will anonymize the path
- /// by replacing it with the given `display`.
- pub fn check_blind(
- &mut self,
- path: &Path,
- display: &str,
- api_name: &str,
- ) -> Result<(), AnyError> {
- skip_check_if_is_permission_fully_granted!(self);
- let desc = ReadDescriptor(resolve_from_cwd(path)?);
- self.check_desc(Some(&desc), false, Some(api_name), || {
- Some(format!("<{display}>"))
- })
+ self.check_desc(Some(desc), false, api_name)
}
pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(None, false, api_name, || None)
+ self.check_desc(None, false, api_name)
}
}
-impl UnaryPermission<WriteDescriptor> {
- pub fn query(&self, path: Option<&Path>) -> PermissionState {
- self.query_desc(
- path
- .map(|p| WriteDescriptor(resolve_from_cwd(p).unwrap()))
- .as_ref(),
- AllowPartial::TreatAsPartialGranted,
- )
+impl UnaryPermission<WriteQueryDescriptor> {
+ pub fn query(&self, path: Option<&WriteQueryDescriptor>) -> PermissionState {
+ self.query_desc(path, AllowPartial::TreatAsPartialGranted)
}
- pub fn request(&mut self, path: Option<&Path>) -> PermissionState {
- self.request_desc(
- path
- .map(|p| WriteDescriptor(resolve_from_cwd(p).unwrap()))
- .as_ref(),
- || Some(path?.display().to_string()),
- )
+ pub fn request(
+ &mut self,
+ path: Option<&WriteQueryDescriptor>,
+ ) -> PermissionState {
+ self.request_desc(path)
}
- pub fn revoke(&mut self, path: Option<&Path>) -> PermissionState {
- self.revoke_desc(
- path
- .map(|p| WriteDescriptor(resolve_from_cwd(p).unwrap()))
- .as_ref(),
- )
+ pub fn revoke(
+ &mut self,
+ path: Option<&WriteQueryDescriptor>,
+ ) -> PermissionState {
+ self.revoke_desc(path)
}
pub fn check(
&mut self,
- path: &Path,
+ path: &WriteQueryDescriptor,
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(
- Some(&WriteDescriptor(resolve_from_cwd(path)?)),
- true,
- api_name,
- || Some(format!("\"{}\"", path.display())),
- )
+ self.check_desc(Some(path), true, api_name)
}
#[inline]
pub fn check_partial(
&mut self,
- path: &Path,
+ path: &WriteQueryDescriptor,
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(
- Some(&WriteDescriptor(resolve_from_cwd(path)?)),
- false,
- api_name,
- || Some(format!("\"{}\"", path.display())),
- )
- }
-
- /// As `check()`, but permission error messages will anonymize the path
- /// by replacing it with the given `display`.
- pub fn check_blind(
- &mut self,
- path: &Path,
- display: &str,
- api_name: &str,
- ) -> Result<(), AnyError> {
- skip_check_if_is_permission_fully_granted!(self);
- let desc = WriteDescriptor(resolve_from_cwd(path)?);
- self.check_desc(Some(&desc), false, Some(api_name), || {
- Some(format!("<{display}>"))
- })
+ self.check_desc(Some(path), false, api_name)
}
pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(None, false, api_name, || None)
+ self.check_desc(None, false, api_name)
}
}
@@ -1203,7 +1499,7 @@ impl UnaryPermission<NetDescriptor> {
}
pub fn request(&mut self, host: Option<&NetDescriptor>) -> PermissionState {
- self.request_desc(host, || None)
+ self.request_desc(host)
}
pub fn revoke(&mut self, host: Option<&NetDescriptor>) -> PermissionState {
@@ -1216,7 +1512,7 @@ impl UnaryPermission<NetDescriptor> {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(Some(host), false, api_name, || None)
+ self.check_desc(Some(host), false, api_name)
}
pub fn check_url(
@@ -1231,14 +1527,12 @@ impl UnaryPermission<NetDescriptor> {
let host = Host::parse(host)?;
let port = url.port_or_known_default();
let descriptor = NetDescriptor(host, port);
- self.check_desc(Some(&descriptor), false, api_name, || {
- Some(format!("\"{descriptor}\""))
- })
+ self.check_desc(Some(&descriptor), false, api_name)
}
pub fn check_all(&mut self) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(None, false, None, || None)
+ self.check_desc(None, false, None)
}
}
@@ -1251,7 +1545,7 @@ impl UnaryPermission<EnvDescriptor> {
}
pub fn request(&mut self, env: Option<&str>) -> PermissionState {
- self.request_desc(env.map(EnvDescriptor::new).as_ref(), || None)
+ self.request_desc(env.map(EnvDescriptor::new).as_ref())
}
pub fn revoke(&mut self, env: Option<&str>) -> PermissionState {
@@ -1264,12 +1558,12 @@ impl UnaryPermission<EnvDescriptor> {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(Some(&EnvDescriptor::new(env)), false, api_name, || None)
+ self.check_desc(Some(&EnvDescriptor::new(env)), false, api_name)
}
pub fn check_all(&mut self) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(None, false, None, || None)
+ self.check_desc(None, false, None)
}
}
@@ -1282,10 +1576,7 @@ impl UnaryPermission<SysDescriptor> {
}
pub fn request(&mut self, kind: Option<&str>) -> PermissionState {
- self
- .request_desc(kind.map(|k| SysDescriptor(k.to_string())).as_ref(), || {
- None
- })
+ self.request_desc(kind.map(|k| SysDescriptor(k.to_string())).as_ref())
}
pub fn revoke(&mut self, kind: Option<&str>) -> PermissionState {
@@ -1298,57 +1589,44 @@ impl UnaryPermission<SysDescriptor> {
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(
- Some(&SysDescriptor(kind.to_string())),
- false,
- api_name,
- || None,
- )
+ self.check_desc(Some(&SysDescriptor(kind.to_string())), false, api_name)
}
pub fn check_all(&mut self) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(None, false, None, || None)
+ self.check_desc(None, false, None)
}
}
-impl UnaryPermission<RunDescriptor> {
- pub fn query(&self, cmd: Option<&str>) -> PermissionState {
- self.query_desc(
- cmd.map(|c| RunDescriptor::from(c.to_string())).as_ref(),
- AllowPartial::TreatAsPartialGranted,
- )
+impl UnaryPermission<RunQueryDescriptor> {
+ pub fn query(&self, cmd: Option<&RunQueryDescriptor>) -> PermissionState {
+ self.query_desc(cmd, AllowPartial::TreatAsPartialGranted)
}
- pub fn request(&mut self, cmd: Option<&str>) -> PermissionState {
- self.request_desc(
- cmd.map(|c| RunDescriptor::from(c.to_string())).as_ref(),
- || Some(cmd?.to_string()),
- )
+ pub fn request(
+ &mut self,
+ cmd: Option<&RunQueryDescriptor>,
+ ) -> PermissionState {
+ self.request_desc(cmd)
}
- pub fn revoke(&mut self, cmd: Option<&str>) -> PermissionState {
- self.revoke_desc(cmd.map(|c| RunDescriptor::from(c.to_string())).as_ref())
+ pub fn revoke(
+ &mut self,
+ cmd: Option<&RunQueryDescriptor>,
+ ) -> PermissionState {
+ self.revoke_desc(cmd)
}
pub fn check(
&mut self,
- cmd: RunPathQuery,
+ cmd: &RunQueryDescriptor,
api_name: Option<&str>,
) -> Result<(), AnyError> {
- debug_assert!(cmd.resolved.is_absolute());
- skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(
- Some(&RunDescriptor::Path(cmd.resolved.to_path_buf())),
- false,
- api_name,
- || Some(format!("\"{}\"", cmd.requested)),
- )
+ self.check_desc(Some(cmd), false, api_name)
}
pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
- skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(None, false, api_name, || None)
+ self.check_desc(None, false, api_name)
}
/// Queries without prompting
@@ -1358,7 +1636,7 @@ impl UnaryPermission<RunDescriptor> {
}
let (result, _prompted, _is_allow_all) =
self.query_desc(None, AllowPartial::TreatAsDenied).check2(
- RunDescriptor::flag_name(),
+ RunQueryDescriptor::flag_name(),
api_name,
|| None,
/* prompt */ false,
@@ -1367,73 +1645,57 @@ impl UnaryPermission<RunDescriptor> {
}
}
-impl UnaryPermission<FfiDescriptor> {
- pub fn query(&self, path: Option<&Path>) -> PermissionState {
- self.query_desc(
- path
- .map(|p| FfiDescriptor(resolve_from_cwd(p).unwrap()))
- .as_ref(),
- AllowPartial::TreatAsPartialGranted,
- )
+impl UnaryPermission<FfiQueryDescriptor> {
+ pub fn query(&self, path: Option<&FfiQueryDescriptor>) -> PermissionState {
+ self.query_desc(path, AllowPartial::TreatAsPartialGranted)
}
- pub fn request(&mut self, path: Option<&Path>) -> PermissionState {
- self.request_desc(
- path
- .map(|p| FfiDescriptor(resolve_from_cwd(p).unwrap()))
- .as_ref(),
- || Some(path?.display().to_string()),
- )
+ pub fn request(
+ &mut self,
+ path: Option<&FfiQueryDescriptor>,
+ ) -> PermissionState {
+ self.request_desc(path)
}
- pub fn revoke(&mut self, path: Option<&Path>) -> PermissionState {
- self.revoke_desc(
- path
- .map(|p| FfiDescriptor(resolve_from_cwd(p).unwrap()))
- .as_ref(),
- )
+ pub fn revoke(
+ &mut self,
+ path: Option<&FfiQueryDescriptor>,
+ ) -> PermissionState {
+ self.revoke_desc(path)
}
pub fn check(
&mut self,
- path: &Path,
+ path: &FfiQueryDescriptor,
api_name: Option<&str>,
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(
- Some(&FfiDescriptor(resolve_from_cwd(path)?)),
- true,
- api_name,
- || Some(format!("\"{}\"", path.display())),
- )
+ self.check_desc(Some(path), true, api_name)
}
- pub fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
+ pub fn check_partial(
+ &mut self,
+ path: Option<&FfiQueryDescriptor>,
+ ) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- let desc = match path {
- Some(path) => Some(FfiDescriptor(resolve_from_cwd(path)?)),
- None => None,
- };
- self.check_desc(desc.as_ref(), false, None, || {
- Some(format!("\"{}\"", path?.display()))
- })
+ self.check_desc(path, false, None)
}
pub fn check_all(&mut self) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
- self.check_desc(None, false, Some("all"), || None)
+ self.check_desc(None, false, Some("all"))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Permissions {
- pub read: UnaryPermission<ReadDescriptor>,
- pub write: UnaryPermission<WriteDescriptor>,
+ pub read: UnaryPermission<ReadQueryDescriptor>,
+ pub write: UnaryPermission<WriteQueryDescriptor>,
pub net: UnaryPermission<NetDescriptor>,
pub env: UnaryPermission<EnvDescriptor>,
pub sys: UnaryPermission<SysDescriptor>,
- pub run: UnaryPermission<RunDescriptor>,
- pub ffi: UnaryPermission<FfiDescriptor>,
+ pub run: UnaryPermission<RunQueryDescriptor>,
+ pub ffi: UnaryPermission<FfiQueryDescriptor>,
pub all: UnitPermission,
}
@@ -1444,33 +1706,33 @@ pub struct PermissionsOptions {
pub deny_env: Option<Vec<String>>,
pub allow_net: Option<Vec<String>>,
pub deny_net: Option<Vec<String>>,
- pub allow_ffi: Option<Vec<PathBuf>>,
- pub deny_ffi: Option<Vec<PathBuf>>,
- pub allow_read: Option<Vec<PathBuf>>,
- pub deny_read: Option<Vec<PathBuf>>,
- pub allow_run: Option<Vec<PathBuf>>,
+ pub allow_ffi: Option<Vec<String>>,
+ pub deny_ffi: Option<Vec<String>>,
+ pub allow_read: Option<Vec<String>>,
+ pub deny_read: Option<Vec<String>>,
+ pub allow_run: Option<Vec<String>>,
pub deny_run: Option<Vec<String>>,
pub allow_sys: Option<Vec<String>>,
pub deny_sys: Option<Vec<String>>,
- pub allow_write: Option<Vec<PathBuf>>,
- pub deny_write: Option<Vec<PathBuf>>,
+ pub allow_write: Option<Vec<String>>,
+ pub deny_write: Option<Vec<String>>,
pub prompt: bool,
}
impl Permissions {
- pub fn new_unary<T>(
- allow_list: Option<&[T::Arg]>,
- deny_list: Option<&[T::Arg]>,
+ pub fn new_unary<TQuery>(
+ allow_list: Option<HashSet<TQuery::AllowDesc>>,
+ deny_list: Option<HashSet<TQuery::DenyDesc>>,
prompt: bool,
- ) -> Result<UnaryPermission<T>, AnyError>
+ ) -> Result<UnaryPermission<TQuery>, AnyError>
where
- T: Descriptor + Hash,
+ TQuery: QueryDescriptor,
{
- Ok(UnaryPermission::<T> {
- granted_global: global_from_option(allow_list),
- granted_list: T::parse(allow_list)?,
- flag_denied_global: global_from_option(deny_list),
- flag_denied_list: T::parse(deny_list)?,
+ Ok(UnaryPermission::<TQuery> {
+ granted_global: global_from_option(allow_list.as_ref()),
+ granted_list: allow_list.unwrap_or_default(),
+ flag_denied_global: global_from_option(deny_list.as_ref()),
+ flag_denied_list: deny_list.unwrap_or_default(),
prompt,
..Default::default()
})
@@ -1486,57 +1748,140 @@ impl Permissions {
)
}
- pub fn from_options(opts: &PermissionsOptions) -> Result<Self, AnyError> {
+ pub fn from_options(
+ parser: &dyn PermissionDescriptorParser,
+ opts: &PermissionsOptions,
+ ) -> Result<Self, AnyError> {
+ fn resolve_allow_run(
+ parser: &dyn PermissionDescriptorParser,
+ allow_run: &[String],
+ ) -> Result<HashSet<AllowRunDescriptor>, AnyError> {
+ let mut new_allow_run = HashSet::with_capacity(allow_run.len());
+ for unresolved in allow_run {
+ if unresolved.is_empty() {
+ bail!("Empty command name not allowed in --allow-run=...")
+ }
+ match parser.parse_allow_run_descriptor(unresolved)? {
+ AllowRunDescriptorParseResult::Descriptor(descriptor) => {
+ new_allow_run.insert(descriptor);
+ }
+ AllowRunDescriptorParseResult::Unresolved(err) => {
+ log::info!(
+ "{} Failed to resolve '{}' for allow-run: {}",
+ colors::gray("Info"),
+ unresolved,
+ err
+ );
+ }
+ }
+ }
+ Ok(new_allow_run)
+ }
+
+ fn parse_maybe_vec<T: Eq + PartialEq + Hash>(
+ items: Option<&[String]>,
+ parse: impl Fn(&str) -> Result<T, AnyError>,
+ ) -> Result<Option<HashSet<T>>, AnyError> {
+ match items {
+ Some(items) => Ok(Some(
+ items
+ .iter()
+ .map(|item| parse(item))
+ .collect::<Result<HashSet<_>, _>>()?,
+ )),
+ None => Ok(None),
+ }
+ }
+
+ let mut deny_write = parse_maybe_vec(opts.deny_write.as_deref(), |item| {
+ parser.parse_write_descriptor(item)
+ })?;
+ let allow_run = opts
+ .allow_run
+ .as_ref()
+ .and_then(|raw_allow_run| {
+ match resolve_allow_run(parser, raw_allow_run) {
+ Ok(resolved_allow_run) => {
+ if resolved_allow_run.is_empty() && !raw_allow_run.is_empty() {
+ None // convert to no permissions if now empty
+ } else {
+ Some(Ok(resolved_allow_run))
+ }
+ }
+ Err(err) => Some(Err(err)),
+ }
+ })
+ .transpose()?;
+ // add the allow_run list to deny_write
+ if let Some(allow_run_vec) = &allow_run {
+ if !allow_run_vec.is_empty() {
+ let deny_write = deny_write.get_or_insert_with(Default::default);
+ deny_write.extend(
+ allow_run_vec
+ .iter()
+ .map(|item| WriteDescriptor(item.0.clone())),
+ );
+ }
+ }
+
Ok(Self {
read: Permissions::new_unary(
- opts.allow_read.as_deref(),
- opts.deny_read.as_deref(),
+ parse_maybe_vec(opts.allow_read.as_deref(), |item| {
+ parser.parse_read_descriptor(item)
+ })?,
+ parse_maybe_vec(opts.deny_read.as_deref(), |item| {
+ parser.parse_read_descriptor(item)
+ })?,
opts.prompt,
)?,
write: Permissions::new_unary(
- opts.allow_write.as_deref(),
- opts.deny_write.as_deref(),
+ parse_maybe_vec(opts.allow_write.as_deref(), |item| {
+ parser.parse_write_descriptor(item)
+ })?,
+ deny_write,
opts.prompt,
)?,
net: Permissions::new_unary(
- opts.allow_net.as_deref(),
- opts.deny_net.as_deref(),
+ parse_maybe_vec(opts.allow_net.as_deref(), |item| {
+ parser.parse_net_descriptor(item)
+ })?,
+ parse_maybe_vec(opts.deny_net.as_deref(), |item| {
+ parser.parse_net_descriptor(item)
+ })?,
opts.prompt,
)?,
env: Permissions::new_unary(
- opts.allow_env.as_deref(),
- opts.deny_env.as_deref(),
+ parse_maybe_vec(opts.allow_env.as_deref(), |item| {
+ parser.parse_env_descriptor(item)
+ })?,
+ parse_maybe_vec(opts.deny_env.as_deref(), |text| {
+ parser.parse_env_descriptor(text)
+ })?,
opts.prompt,
)?,
sys: Permissions::new_unary(
- opts.allow_sys.as_deref(),
- opts.deny_sys.as_deref(),
+ parse_maybe_vec(opts.allow_sys.as_deref(), |text| {
+ parser.parse_sys_descriptor(text)
+ })?,
+ parse_maybe_vec(opts.deny_sys.as_deref(), |text| {
+ parser.parse_sys_descriptor(text)
+ })?,
opts.prompt,
)?,
run: Permissions::new_unary(
- opts
- .allow_run
- .as_ref()
- .map(|d| {
- d.iter()
- .map(|s| RunDescriptorArg::Path(s.clone()))
- .collect::<Vec<_>>()
- })
- .as_deref(),
- opts
- .deny_run
- .as_ref()
- .map(|d| {
- d.iter()
- .map(|s| RunDescriptorArg::from(s.clone()))
- .collect::<Vec<_>>()
- })
- .as_deref(),
+ allow_run,
+ parse_maybe_vec(opts.deny_run.as_deref(), |text| {
+ parser.parse_deny_run_descriptor(text)
+ })?,
opts.prompt,
)?,
ffi: Permissions::new_unary(
- opts.allow_ffi.as_deref(),
- opts.deny_ffi.as_deref(),
+ parse_maybe_vec(opts.allow_ffi.as_deref(), |text| {
+ parser.parse_ffi_descriptor(text)
+ })?,
+ parse_maybe_vec(opts.deny_ffi.as_deref(), |text| {
+ parser.parse_ffi_descriptor(text)
+ })?,
opts.prompt,
)?,
all: Permissions::new_all(opts.allow_all),
@@ -1588,7 +1933,14 @@ impl Permissions {
) -> Result<(), AnyError> {
match specifier.scheme() {
"file" => match specifier.to_file_path() {
- Ok(path) => self.read.check(&path, Some("import()")),
+ Ok(path) => self.read.check(
+ &PathQueryDescriptor {
+ requested: path.to_string_lossy().into_owned(),
+ resolved: path,
+ }
+ .into_read(),
+ Some("import()"),
+ ),
Err(_) => Err(uri_error(format!(
"Invalid file path.\n Specifier: {specifier}"
))),
@@ -1607,15 +1959,30 @@ impl Permissions {
/// case might need to be mutated). Also for the Web Worker API we need a way
/// to send permissions to a new thread.
#[derive(Clone, Debug)]
-pub struct PermissionsContainer(pub Arc<Mutex<Permissions>>);
+pub struct PermissionsContainer {
+ // todo(dsherret): make both of these private as the functionality
+ // can just be methods on PermissionsContainer. Additionally, a separate
+ // struct should be created in here that handles creating child permissions
+ // so that the code is not so verbose elsewhere.
+ pub descriptor_parser: Arc<dyn PermissionDescriptorParser>,
+ pub inner: Arc<Mutex<Permissions>>,
+}
impl PermissionsContainer {
- pub fn new(perms: Permissions) -> Self {
- Self(Arc::new(Mutex::new(perms)))
+ pub fn new(
+ descriptor_parser: Arc<dyn PermissionDescriptorParser>,
+ perms: Permissions,
+ ) -> Self {
+ Self {
+ descriptor_parser,
+ inner: Arc::new(Mutex::new(perms)),
+ }
}
- pub fn allow_all() -> Self {
- Self::new(Permissions::allow_all())
+ pub fn allow_all(
+ descriptor_parser: Arc<dyn PermissionDescriptorParser>,
+ ) -> Self {
+ Self::new(descriptor_parser, Permissions::allow_all())
}
#[inline(always)]
@@ -1623,27 +1990,61 @@ impl PermissionsContainer {
&self,
specifier: &ModuleSpecifier,
) -> Result<(), AnyError> {
- self.0.lock().check_specifier(specifier)
+ self.inner.lock().check_specifier(specifier)
}
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)]
pub fn check_read(
- &mut self,
- path: &Path,
+ &self,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
- self.0.lock().read.check(path, Some(api_name))
+ ) -> Result<PathBuf, AnyError> {
+ self.check_read_with_api_name(path, Some(api_name))
}
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)]
pub fn check_read_with_api_name(
&self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
- self.0.lock().read.check(path, api_name)
+ ) -> Result<PathBuf, AnyError> {
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.read;
+ if inner.is_allow_all() {
+ Ok(PathBuf::from(path))
+ } else {
+ let desc = self.descriptor_parser.parse_path_query(path)?.into_read();
+ inner.check(&desc, api_name)?;
+ Ok(desc.0.resolved)
+ }
+ }
+
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ #[inline(always)]
+ pub fn check_read_path<'a>(
+ &self,
+ path: &'a Path,
+ api_name: Option<&str>,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.read;
+ if inner.is_allow_all() {
+ Ok(Cow::Borrowed(path))
+ } else {
+ let desc = PathQueryDescriptor {
+ requested: path.to_string_lossy().into_owned(),
+ resolved: path.to_path_buf(),
+ }
+ .into_read();
+ inner.check(&desc, api_name)?;
+ Ok(Cow::Owned(desc.0.resolved))
+ }
}
+ /// As `check_read()`, but permission error messages will anonymize the path
+ /// by replacing it with the given `display`.
#[inline(always)]
pub fn check_read_blind(
&mut self,
@@ -1651,105 +2052,167 @@ impl PermissionsContainer {
display: &str,
api_name: &str,
) -> Result<(), AnyError> {
- self.0.lock().read.check_blind(path, display, api_name)
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.read;
+ skip_check_if_is_permission_fully_granted!(inner);
+ inner.check(
+ &PathQueryDescriptor {
+ requested: format!("<{}>", display),
+ resolved: path.to_path_buf(),
+ }
+ .into_read(),
+ Some(api_name),
+ )
}
#[inline(always)]
- pub fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError> {
- self.0.lock().read.check_all(Some(api_name))
+ pub fn check_read_all(&self, api_name: &str) -> Result<(), AnyError> {
+ self.inner.lock().read.check_all(Some(api_name))
}
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)]
pub fn check_write(
- &mut self,
- path: &Path,
+ &self,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
- self.0.lock().write.check(path, Some(api_name))
+ ) -> Result<PathBuf, AnyError> {
+ self.check_write_with_api_name(path, Some(api_name))
}
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)]
pub fn check_write_with_api_name(
&self,
- path: &Path,
+ path: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
- self.0.lock().write.check(path, api_name)
+ ) -> Result<PathBuf, AnyError> {
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.write;
+ if inner.is_allow_all() {
+ Ok(PathBuf::from(path))
+ } else {
+ let desc = self.descriptor_parser.parse_path_query(path)?.into_write();
+ inner.check(&desc, api_name)?;
+ Ok(desc.0.resolved)
+ }
+ }
+
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ #[inline(always)]
+ pub fn check_write_path<'a>(
+ &self,
+ path: &'a Path,
+ api_name: &str,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.write;
+ if inner.is_allow_all() {
+ Ok(Cow::Borrowed(path))
+ } else {
+ let desc = PathQueryDescriptor {
+ requested: path.to_string_lossy().into_owned(),
+ resolved: path.to_path_buf(),
+ }
+ .into_write();
+ inner.check(&desc, Some(api_name))?;
+ Ok(Cow::Owned(desc.0.resolved))
+ }
}
#[inline(always)]
- pub fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError> {
- self.0.lock().write.check_all(Some(api_name))
+ pub fn check_write_all(&self, api_name: &str) -> Result<(), AnyError> {
+ self.inner.lock().write.check_all(Some(api_name))
}
+ /// As `check_write()`, but permission error messages will anonymize the path
+ /// by replacing it with the given `display`.
#[inline(always)]
pub fn check_write_blind(
- &mut self,
+ &self,
path: &Path,
display: &str,
api_name: &str,
) -> Result<(), AnyError> {
- self.0.lock().write.check_blind(path, display, api_name)
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.write;
+ skip_check_if_is_permission_fully_granted!(inner);
+ inner.check(
+ &PathQueryDescriptor {
+ requested: format!("<{}>", display),
+ resolved: path.to_path_buf(),
+ }
+ .into_write(),
+ Some(api_name),
+ )
}
#[inline(always)]
pub fn check_write_partial(
&mut self,
- path: &Path,
+ path: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
- self.0.lock().write.check_partial(path, Some(api_name))
+ ) -> Result<PathBuf, AnyError> {
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.write;
+ if inner.is_allow_all() {
+ Ok(PathBuf::from(path))
+ } else {
+ let desc = self.descriptor_parser.parse_path_query(path)?.into_write();
+ inner.check_partial(&desc, Some(api_name))?;
+ Ok(desc.0.resolved)
+ }
}
#[inline(always)]
pub fn check_run(
&mut self,
- cmd: RunPathQuery,
+ cmd: &RunQueryDescriptor,
api_name: &str,
) -> Result<(), AnyError> {
- self.0.lock().run.check(cmd, Some(api_name))
+ self.inner.lock().run.check(cmd, Some(api_name))
}
#[inline(always)]
pub fn check_run_all(&mut self, api_name: &str) -> Result<(), AnyError> {
- self.0.lock().run.check_all(Some(api_name))
+ self.inner.lock().run.check_all(Some(api_name))
}
#[inline(always)]
pub fn query_run_all(&mut self, api_name: &str) -> bool {
- self.0.lock().run.query_all(Some(api_name))
+ self.inner.lock().run.query_all(Some(api_name))
}
#[inline(always)]
pub fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> {
- self.0.lock().sys.check(kind, Some(api_name))
+ self.inner.lock().sys.check(kind, Some(api_name))
}
#[inline(always)]
pub fn check_env(&mut self, var: &str) -> Result<(), AnyError> {
- self.0.lock().env.check(var, None)
+ self.inner.lock().env.check(var, None)
}
#[inline(always)]
pub fn check_env_all(&mut self) -> Result<(), AnyError> {
- self.0.lock().env.check_all()
+ self.inner.lock().env.check_all()
}
#[inline(always)]
pub fn check_sys_all(&mut self) -> Result<(), AnyError> {
- self.0.lock().sys.check_all()
+ self.inner.lock().sys.check_all()
}
#[inline(always)]
pub fn check_ffi_all(&mut self) -> Result<(), AnyError> {
- self.0.lock().ffi.check_all()
+ self.inner.lock().ffi.check_all()
}
/// This checks to see if the allow-all flag was passed, not whether all
/// permissions are enabled!
#[inline(always)]
pub fn check_was_allow_all_flag_passed(&mut self) -> Result<(), AnyError> {
- self.0.lock().all.check()
+ self.inner.lock().all.check()
}
/// Checks special file access, returning the failed permission type if
@@ -1862,7 +2325,7 @@ impl PermissionsContainer {
url: &Url,
api_name: &str,
) -> Result<(), AnyError> {
- self.0.lock().net.check_url(url, Some(api_name))
+ self.inner.lock().net.check_url(url, Some(api_name))
}
#[inline(always)]
@@ -1871,22 +2334,54 @@ impl PermissionsContainer {
host: &(T, Option<u16>),
api_name: &str,
) -> Result<(), AnyError> {
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.net;
+ skip_check_if_is_permission_fully_granted!(inner);
let hostname = Host::parse(host.0.as_ref())?;
let descriptor = NetDescriptor(hostname, host.1);
- self.0.lock().net.check(&descriptor, Some(api_name))
+ inner.check(&descriptor, Some(api_name))
}
#[inline(always)]
- pub fn check_ffi(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
- self.0.lock().ffi.check(path.unwrap(), None)
+ pub fn check_ffi(&mut self, path: &str) -> Result<PathBuf, AnyError> {
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.ffi;
+ if inner.is_allow_all() {
+ Ok(PathBuf::from(path))
+ } else {
+ let desc = self.descriptor_parser.parse_path_query(path)?.into_ffi();
+ inner.check(&desc, None)?;
+ Ok(desc.0.resolved)
+ }
+ }
+
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
+ #[inline(always)]
+ pub fn check_ffi_partial_no_path(&mut self) -> Result<(), AnyError> {
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.ffi;
+ if inner.is_allow_all() {
+ Ok(())
+ } else {
+ inner.check_partial(None)
+ }
}
+ #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)]
- pub fn check_ffi_partial(
+ pub fn check_ffi_partial_with_path(
&mut self,
- path: Option<&Path>,
- ) -> Result<(), AnyError> {
- self.0.lock().ffi.check_partial(path)
+ path: &str,
+ ) -> Result<PathBuf, AnyError> {
+ let mut inner = self.inner.lock();
+ let inner = &mut inner.ffi;
+ if inner.is_allow_all() {
+ Ok(PathBuf::from(path))
+ } else {
+ let desc = self.descriptor_parser.parse_path_query(path)?.into_ffi();
+ inner.check_partial(Some(&desc))?;
+ Ok(desc.0.resolved)
+ }
}
}
@@ -1911,93 +2406,10 @@ const fn unit_permission_from_flag_bools(
}
}
-fn global_from_option<T>(flag: Option<&[T]>) -> bool {
+fn global_from_option<T>(flag: Option<&HashSet<T>>) -> bool {
matches!(flag, Some(v) if v.is_empty())
}
-fn parse_net_list(
- list: Option<&[String]>,
-) -> Result<HashSet<NetDescriptor>, AnyError> {
- if let Some(v) = list {
- v.iter()
- .map(|x| NetDescriptor::parse(x))
- .collect::<Result<HashSet<NetDescriptor>, AnyError>>()
- } else {
- Ok(HashSet::new())
- }
-}
-
-fn parse_env_list(
- list: Option<&[String]>,
-) -> Result<HashSet<EnvDescriptor>, AnyError> {
- if let Some(v) = list {
- v.iter()
- .map(|x| {
- if x.is_empty() {
- Err(AnyError::msg("Empty path is not allowed"))
- } else {
- Ok(EnvDescriptor::new(x))
- }
- })
- .collect()
- } else {
- Ok(HashSet::new())
- }
-}
-
-fn parse_path_list<T: Descriptor + Hash>(
- list: Option<&[PathBuf]>,
- f: fn(PathBuf) -> T,
-) -> Result<HashSet<T>, AnyError> {
- if let Some(v) = list {
- v.iter()
- .map(|raw_path| {
- if raw_path.as_os_str().is_empty() {
- Err(AnyError::msg("Empty path is not allowed"))
- } else {
- resolve_from_cwd(Path::new(&raw_path)).map(f)
- }
- })
- .collect()
- } else {
- Ok(HashSet::new())
- }
-}
-
-fn parse_sys_list(
- list: Option<&[String]>,
-) -> Result<HashSet<SysDescriptor>, AnyError> {
- if let Some(v) = list {
- v.iter()
- .map(|x| {
- if x.is_empty() {
- Err(AnyError::msg("empty"))
- } else {
- Ok(SysDescriptor(x.to_string()))
- }
- })
- .collect()
- } else {
- Ok(HashSet::new())
- }
-}
-
-fn parse_run_list(
- list: Option<&[RunDescriptorArg]>,
-) -> Result<HashSet<RunDescriptor>, AnyError> {
- let Some(v) = list else {
- return Ok(HashSet::new());
- };
- Ok(
- v.iter()
- .map(|arg| match arg {
- RunDescriptorArg::Name(s) => RunDescriptor::Name(s.clone()),
- RunDescriptorArg::Path(l) => RunDescriptor::Path(l.clone()),
- })
- .collect(),
- )
-}
-
fn escalation_error() -> AnyError {
custom_error("NotCapable", "Can't escalate parent thread permissions")
}
@@ -2246,7 +2658,58 @@ impl<'de> Deserialize<'de> for ChildPermissionsArg {
}
}
+/// Parses and normalizes permissions.
+///
+/// This trait is necessary because this crate doesn't have access
+/// to the file system.
+pub trait PermissionDescriptorParser: Debug + Send + Sync {
+ fn parse_read_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<ReadDescriptor, AnyError>;
+
+ fn parse_write_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<WriteDescriptor, AnyError>;
+
+ fn parse_net_descriptor(&self, text: &str)
+ -> Result<NetDescriptor, AnyError>;
+
+ fn parse_env_descriptor(&self, text: &str)
+ -> Result<EnvDescriptor, AnyError>;
+
+ fn parse_sys_descriptor(&self, text: &str)
+ -> Result<SysDescriptor, AnyError>;
+
+ fn parse_allow_run_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<AllowRunDescriptorParseResult, AnyError>;
+
+ fn parse_deny_run_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<DenyRunDescriptor, AnyError>;
+
+ fn parse_ffi_descriptor(&self, text: &str)
+ -> Result<FfiDescriptor, AnyError>;
+
+ // queries
+
+ fn parse_path_query(
+ &self,
+ path: &str,
+ ) -> Result<PathQueryDescriptor, AnyError>;
+
+ fn parse_run_query(
+ &self,
+ requested: &str,
+ ) -> Result<RunQueryDescriptor, AnyError>;
+}
+
pub fn create_child_permissions(
+ parser: &dyn PermissionDescriptorParser,
main_perms: &mut Permissions,
child_permissions_arg: ChildPermissionsArg,
) -> Result<Permissions, AnyError> {
@@ -2287,25 +2750,41 @@ pub fn create_child_permissions(
// in the worker_perms.all block above
worker_perms.read = main_perms
.read
- .create_child_permissions(child_permissions_arg.read)?;
+ .create_child_permissions(child_permissions_arg.read, |text| {
+ Ok(Some(parser.parse_read_descriptor(text)?))
+ })?;
worker_perms.write = main_perms
.write
- .create_child_permissions(child_permissions_arg.write)?;
+ .create_child_permissions(child_permissions_arg.write, |text| {
+ Ok(Some(parser.parse_write_descriptor(text)?))
+ })?;
worker_perms.net = main_perms
.net
- .create_child_permissions(child_permissions_arg.net)?;
+ .create_child_permissions(child_permissions_arg.net, |text| {
+ Ok(Some(parser.parse_net_descriptor(text)?))
+ })?;
worker_perms.env = main_perms
.env
- .create_child_permissions(child_permissions_arg.env)?;
+ .create_child_permissions(child_permissions_arg.env, |text| {
+ Ok(Some(parser.parse_env_descriptor(text)?))
+ })?;
worker_perms.sys = main_perms
.sys
- .create_child_permissions(child_permissions_arg.sys)?;
- worker_perms.run = main_perms
- .run
- .create_child_permissions(child_permissions_arg.run)?;
+ .create_child_permissions(child_permissions_arg.sys, |text| {
+ Ok(Some(parser.parse_sys_descriptor(text)?))
+ })?;
+ worker_perms.run = main_perms.run.create_child_permissions(
+ child_permissions_arg.run,
+ |text| match parser.parse_allow_run_descriptor(text)? {
+ AllowRunDescriptorParseResult::Unresolved(_) => Ok(None),
+ AllowRunDescriptorParseResult::Descriptor(desc) => Ok(Some(desc)),
+ },
+ )?;
worker_perms.ffi = main_perms
.ffi
- .create_child_permissions(child_permissions_arg.ffi)?;
+ .create_child_permissions(child_permissions_arg.ffi, |text| {
+ Ok(Some(parser.parse_ffi_descriptor(text)?))
+ })?;
Ok(worker_perms)
}
@@ -2332,128 +2811,167 @@ mod tests {
macro_rules! svec {
($($x:expr),*) => (vec![$($x.to_string()),*]);
}
- macro_rules! sarr {
- ($($x:expr),*) => ([$($x.to_string()),*]);
+
+ #[derive(Debug)]
+ struct TestPermissionDescriptorParser;
+
+ impl TestPermissionDescriptorParser {
+ fn join_path_with_root(&self, path: &str) -> PathBuf {
+ if path.starts_with("C:\\") {
+ PathBuf::from(path)
+ } else {
+ PathBuf::from("/").join(path)
+ }
+ }
}
- #[test]
- fn check_paths() {
- set_prompter(Box::new(TestPrompter));
- let allowlist = vec![
- PathBuf::from("/a/specific/dir/name"),
- PathBuf::from("/a/specific"),
- PathBuf::from("/b/c"),
- ];
+ impl PermissionDescriptorParser for TestPermissionDescriptorParser {
+ fn parse_read_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<ReadDescriptor, AnyError> {
+ Ok(ReadDescriptor(self.join_path_with_root(text)))
+ }
- let mut perms = Permissions::from_options(&PermissionsOptions {
- allow_read: Some(allowlist.clone()),
- allow_write: Some(allowlist.clone()),
- allow_ffi: Some(allowlist),
- ..Default::default()
- })
- .unwrap();
+ fn parse_write_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<WriteDescriptor, AnyError> {
+ Ok(WriteDescriptor(self.join_path_with_root(text)))
+ }
- // Inside of /a/specific and /a/specific/dir/name
- assert!(perms
- .read
- .check(Path::new("/a/specific/dir/name"), None)
- .is_ok());
- assert!(perms
- .write
- .check(Path::new("/a/specific/dir/name"), None)
- .is_ok());
- assert!(perms
- .ffi
- .check(Path::new("/a/specific/dir/name"), None)
- .is_ok());
+ fn parse_net_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<NetDescriptor, AnyError> {
+ NetDescriptor::parse(text)
+ }
- // Inside of /a/specific but outside of /a/specific/dir/name
- assert!(perms.read.check(Path::new("/a/specific/dir"), None).is_ok());
- assert!(perms
- .write
- .check(Path::new("/a/specific/dir"), None)
- .is_ok());
- assert!(perms.ffi.check(Path::new("/a/specific/dir"), None).is_ok());
+ fn parse_env_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<EnvDescriptor, AnyError> {
+ Ok(EnvDescriptor::new(text))
+ }
- // Inside of /a/specific and /a/specific/dir/name
- assert!(perms
- .read
- .check(Path::new("/a/specific/dir/name/inner"), None)
- .is_ok());
- assert!(perms
- .write
- .check(Path::new("/a/specific/dir/name/inner"), None)
- .is_ok());
- assert!(perms
- .ffi
- .check(Path::new("/a/specific/dir/name/inner"), None)
- .is_ok());
+ fn parse_sys_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<SysDescriptor, AnyError> {
+ Ok(SysDescriptor(text.to_string()))
+ }
- // Inside of /a/specific but outside of /a/specific/dir/name
- assert!(perms
- .read
- .check(Path::new("/a/specific/other/dir"), None)
- .is_ok());
- assert!(perms
- .write
- .check(Path::new("/a/specific/other/dir"), None)
- .is_ok());
- assert!(perms
- .ffi
- .check(Path::new("/a/specific/other/dir"), None)
- .is_ok());
+ fn parse_allow_run_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<AllowRunDescriptorParseResult, AnyError> {
+ Ok(AllowRunDescriptorParseResult::Descriptor(
+ AllowRunDescriptor(self.join_path_with_root(text)),
+ ))
+ }
- // Exact match with /b/c
- assert!(perms.read.check(Path::new("/b/c"), None).is_ok());
- assert!(perms.write.check(Path::new("/b/c"), None).is_ok());
- assert!(perms.ffi.check(Path::new("/b/c"), None).is_ok());
+ fn parse_deny_run_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<DenyRunDescriptor, AnyError> {
+ if text.contains("/") {
+ Ok(DenyRunDescriptor::Path(self.join_path_with_root(text)))
+ } else {
+ Ok(DenyRunDescriptor::Name(text.to_string()))
+ }
+ }
- // Sub path within /b/c
- assert!(perms.read.check(Path::new("/b/c/sub/path"), None).is_ok());
- assert!(perms.write.check(Path::new("/b/c/sub/path"), None).is_ok());
- assert!(perms.ffi.check(Path::new("/b/c/sub/path"), None).is_ok());
+ fn parse_ffi_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<FfiDescriptor, AnyError> {
+ Ok(FfiDescriptor(self.join_path_with_root(text)))
+ }
- // Sub path within /b/c, needs normalizing
- assert!(perms
- .read
- .check(Path::new("/b/c/sub/path/../path/."), None)
- .is_ok());
- assert!(perms
- .write
- .check(Path::new("/b/c/sub/path/../path/."), None)
- .is_ok());
- assert!(perms
- .ffi
- .check(Path::new("/b/c/sub/path/../path/."), None)
- .is_ok());
+ fn parse_path_query(
+ &self,
+ path: &str,
+ ) -> Result<PathQueryDescriptor, AnyError> {
+ Ok(PathQueryDescriptor {
+ resolved: self.join_path_with_root(path),
+ requested: path.to_string(),
+ })
+ }
- // Inside of /b but outside of /b/c
- assert!(perms.read.check(Path::new("/b/e"), None).is_err());
- assert!(perms.write.check(Path::new("/b/e"), None).is_err());
- assert!(perms.ffi.check(Path::new("/b/e"), None).is_err());
+ fn parse_run_query(
+ &self,
+ requested: &str,
+ ) -> Result<RunQueryDescriptor, AnyError> {
+ RunQueryDescriptor::parse(requested)
+ }
+ }
- // Inside of /a but outside of /a/specific
- assert!(perms.read.check(Path::new("/a/b"), None).is_err());
- assert!(perms.write.check(Path::new("/a/b"), None).is_err());
- assert!(perms.ffi.check(Path::new("/a/b"), None).is_err());
+ #[test]
+ fn check_paths() {
+ set_prompter(Box::new(TestPrompter));
+ let allowlist = svec!["/a/specific/dir/name", "/a/specific", "/b/c"];
+
+ let parser = TestPermissionDescriptorParser;
+ let perms = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_read: Some(allowlist.clone()),
+ allow_write: Some(allowlist.clone()),
+ allow_ffi: Some(allowlist),
+ ..Default::default()
+ },
+ )
+ .unwrap();
+ let mut perms = PermissionsContainer::new(Arc::new(parser), perms);
+
+ let cases = [
+ // Inside of /a/specific and /a/specific/dir/name
+ ("/a/specific/dir/name", true),
+ // Inside of /a/specific but outside of /a/specific/dir/name
+ ("/a/specific/dir", true),
+ // Inside of /a/specific and /a/specific/dir/name
+ ("/a/specific/dir/name/inner", true),
+ // Inside of /a/specific but outside of /a/specific/dir/name
+ ("/a/specific/other/dir", true),
+ // Exact match with /b/c
+ ("/b/c", true),
+ // Sub path within /b/c
+ ("/b/c/sub/path", true),
+ // Sub path within /b/c, needs normalizing
+ ("/b/c/sub/path/../path/.", true),
+ // Inside of /b but outside of /b/c
+ ("/b/e", false),
+ // Inside of /a but outside of /a/specific
+ ("/a/b", false),
+ ];
+
+ for (path, is_ok) in cases {
+ assert_eq!(perms.check_read(path, "api").is_ok(), is_ok);
+ assert_eq!(perms.check_write(path, "api").is_ok(), is_ok);
+ assert_eq!(perms.check_ffi(path).is_ok(), is_ok);
+ }
}
#[test]
fn test_check_net_with_values() {
set_prompter(Box::new(TestPrompter));
- let mut perms = Permissions::from_options(&PermissionsOptions {
- allow_net: Some(svec![
- "localhost",
- "deno.land",
- "github.com:3000",
- "127.0.0.1",
- "172.16.0.2:8000",
- "www.github.com:443",
- "80.example.com:80",
- "443.example.com:443"
- ]),
- ..Default::default()
- })
+ let parser = TestPermissionDescriptorParser;
+ let mut perms = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_net: Some(svec![
+ "localhost",
+ "deno.land",
+ "github.com:3000",
+ "127.0.0.1",
+ "172.16.0.2:8000",
+ "www.github.com:443",
+ "80.example.com:80",
+ "443.example.com:443"
+ ]),
+ ..Default::default()
+ },
+ )
.unwrap();
let domain_tests = vec![
@@ -2496,10 +3014,14 @@ mod tests {
#[test]
fn test_check_net_only_flag() {
set_prompter(Box::new(TestPrompter));
- let mut perms = Permissions::from_options(&PermissionsOptions {
- allow_net: Some(svec![]), // this means `--allow-net` is present without values following `=` sign
- ..Default::default()
- })
+ let parser = TestPermissionDescriptorParser;
+ let mut perms = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_net: Some(svec![]), // this means `--allow-net` is present without values following `=` sign
+ ..Default::default()
+ },
+ )
.unwrap();
let domain_tests = vec![
@@ -2537,10 +3059,14 @@ mod tests {
#[test]
fn test_check_net_no_flag() {
set_prompter(Box::new(TestPrompter));
- let mut perms = Permissions::from_options(&PermissionsOptions {
- allow_net: None,
- ..Default::default()
- })
+ let parser = TestPermissionDescriptorParser;
+ let mut perms = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_net: None,
+ ..Default::default()
+ },
+ )
.unwrap();
let domain_tests = vec![
@@ -2577,17 +3103,20 @@ mod tests {
#[test]
fn test_check_net_url() {
- let mut perms = Permissions::from_options(&PermissionsOptions {
- allow_net: Some(svec![
- "localhost",
- "deno.land",
- "github.com:3000",
- "127.0.0.1",
- "172.16.0.2:8000",
- "www.github.com:443"
- ]),
- ..Default::default()
- })
+ let mut perms = Permissions::from_options(
+ &TestPermissionDescriptorParser,
+ &PermissionsOptions {
+ allow_net: Some(svec![
+ "localhost",
+ "deno.land",
+ "github.com:3000",
+ "127.0.0.1",
+ "172.16.0.2:8000",
+ "www.github.com:443"
+ ]),
+ ..Default::default()
+ },
+ )
.unwrap();
let url_tests = vec![
@@ -2639,15 +3168,18 @@ mod tests {
fn check_specifiers() {
set_prompter(Box::new(TestPrompter));
let read_allowlist = if cfg!(target_os = "windows") {
- vec![PathBuf::from("C:\\a")]
+ svec!["C:\\a"]
} else {
- vec![PathBuf::from("/a")]
+ svec!["/a"]
};
- let mut perms = Permissions::from_options(&PermissionsOptions {
- allow_read: Some(read_allowlist),
- allow_net: Some(svec!["localhost"]),
- ..Default::default()
- })
+ let mut perms = Permissions::from_options(
+ &TestPermissionDescriptorParser,
+ &PermissionsOptions {
+ allow_read: Some(read_allowlist),
+ allow_net: Some(svec!["localhost"]),
+ ..Default::default()
+ },
+ )
.unwrap();
let mut fixtures = vec![
@@ -2713,130 +3245,99 @@ mod tests {
#[test]
fn test_query() {
set_prompter(Box::new(TestPrompter));
+ let parser = TestPermissionDescriptorParser;
let perms1 = Permissions::allow_all();
- let perms2 = Permissions {
- read: Permissions::new_unary(Some(&[PathBuf::from("/foo")]), None, false)
- .unwrap(),
- write: Permissions::new_unary(
- Some(&[PathBuf::from("/foo")]),
- None,
- false,
- )
- .unwrap(),
- ffi: Permissions::new_unary(Some(&[PathBuf::from("/foo")]), None, false)
- .unwrap(),
- net: Permissions::new_unary(Some(&sarr!["127.0.0.1:8000"]), None, false)
- .unwrap(),
- env: Permissions::new_unary(Some(&sarr!["HOME"]), None, false).unwrap(),
- sys: Permissions::new_unary(Some(&sarr!["hostname"]), None, false)
- .unwrap(),
- run: Permissions::new_unary(
- Some(&["deno".to_string().into()]),
- None,
- false,
- )
- .unwrap(),
- all: Permissions::new_all(false),
- };
- let perms3 = Permissions {
- read: Permissions::new_unary(None, Some(&[PathBuf::from("/foo")]), false)
- .unwrap(),
- write: Permissions::new_unary(
- None,
- Some(&[PathBuf::from("/foo")]),
- false,
- )
- .unwrap(),
- ffi: Permissions::new_unary(None, Some(&[PathBuf::from("/foo")]), false)
- .unwrap(),
- net: Permissions::new_unary(None, Some(&sarr!["127.0.0.1:8000"]), false)
- .unwrap(),
- env: Permissions::new_unary(None, Some(&sarr!["HOME"]), false).unwrap(),
- sys: Permissions::new_unary(None, Some(&sarr!["hostname"]), false)
- .unwrap(),
- run: Permissions::new_unary(
- None,
- Some(&["deno".to_string().into()]),
- false,
- )
- .unwrap(),
- all: Permissions::new_all(false),
- };
- let perms4 = Permissions {
- read: Permissions::new_unary(
- Some(&[]),
- Some(&[PathBuf::from("/foo")]),
- false,
- )
- .unwrap(),
- write: Permissions::new_unary(
- Some(&[]),
- Some(&[PathBuf::from("/foo")]),
- false,
- )
- .unwrap(),
- ffi: Permissions::new_unary(
- Some(&[]),
- Some(&[PathBuf::from("/foo")]),
- false,
- )
- .unwrap(),
- net: Permissions::new_unary(
- Some(&[]),
- Some(&sarr!["127.0.0.1:8000"]),
- false,
- )
- .unwrap(),
- env: Permissions::new_unary(Some(&[]), Some(&sarr!["HOME"]), false)
- .unwrap(),
- sys: Permissions::new_unary(Some(&[]), Some(&sarr!["hostname"]), false)
- .unwrap(),
- run: Permissions::new_unary(
- Some(&[]),
- Some(&["deno".to_string().into()]),
- false,
- )
- .unwrap(),
- all: Permissions::new_all(false),
- };
+ let perms2 = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_read: Some(svec!["/foo"]),
+ allow_write: Some(svec!["/foo"]),
+ allow_ffi: Some(svec!["/foo"]),
+ allow_net: Some(svec!["127.0.0.1:8000"]),
+ allow_env: Some(svec!["HOME"]),
+ allow_sys: Some(svec!["hostname"]),
+ allow_run: Some(svec!["/deno"]),
+ allow_all: false,
+ ..Default::default()
+ },
+ )
+ .unwrap();
+ let perms3 = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ deny_read: Some(svec!["/foo"]),
+ deny_write: Some(svec!["/foo"]),
+ deny_ffi: Some(svec!["/foo"]),
+ deny_net: Some(svec!["127.0.0.1:8000"]),
+ deny_env: Some(svec!["HOME"]),
+ deny_sys: Some(svec!["hostname"]),
+ deny_run: Some(svec!["deno"]),
+ ..Default::default()
+ },
+ )
+ .unwrap();
+ let perms4 = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_read: Some(vec![]),
+ deny_read: Some(svec!["/foo"]),
+ allow_write: Some(vec![]),
+ deny_write: Some(svec!["/foo"]),
+ allow_ffi: Some(vec![]),
+ deny_ffi: Some(svec!["/foo"]),
+ allow_net: Some(vec![]),
+ deny_net: Some(svec!["127.0.0.1:8000"]),
+ allow_env: Some(vec![]),
+ deny_env: Some(svec!["HOME"]),
+ allow_sys: Some(vec![]),
+ deny_sys: Some(svec!["hostname"]),
+ allow_run: Some(vec![]),
+ deny_run: Some(svec!["deno"]),
+ ..Default::default()
+ },
+ )
+ .unwrap();
#[rustfmt::skip]
{
+ let read_query = |path: &str| parser.parse_path_query(path).unwrap().into_read();
+ let write_query = |path: &str| parser.parse_path_query(path).unwrap().into_write();
+ let ffi_query = |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
assert_eq!(perms1.read.query(None), PermissionState::Granted);
- assert_eq!(perms1.read.query(Some(Path::new("/foo"))), PermissionState::Granted);
+ assert_eq!(perms1.read.query(Some(&read_query("/foo"))), PermissionState::Granted);
assert_eq!(perms2.read.query(None), PermissionState::Prompt);
- assert_eq!(perms2.read.query(Some(Path::new("/foo"))), PermissionState::Granted);
- assert_eq!(perms2.read.query(Some(Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms2.read.query(Some(&read_query("/foo"))), PermissionState::Granted);
+ assert_eq!(perms2.read.query(Some(&read_query("/foo/bar"))), PermissionState::Granted);
assert_eq!(perms3.read.query(None), PermissionState::Prompt);
- assert_eq!(perms3.read.query(Some(Path::new("/foo"))), PermissionState::Denied);
- assert_eq!(perms3.read.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms3.read.query(Some(&read_query("/foo"))), PermissionState::Denied);
+ assert_eq!(perms3.read.query(Some(&read_query("/foo/bar"))), PermissionState::Denied);
assert_eq!(perms4.read.query(None), PermissionState::GrantedPartial);
- assert_eq!(perms4.read.query(Some(Path::new("/foo"))), PermissionState::Denied);
- assert_eq!(perms4.read.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
- assert_eq!(perms4.read.query(Some(Path::new("/bar"))), PermissionState::Granted);
+ assert_eq!(perms4.read.query(Some(&read_query("/foo"))), PermissionState::Denied);
+ assert_eq!(perms4.read.query(Some(&read_query("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms4.read.query(Some(&read_query("/bar"))), PermissionState::Granted);
assert_eq!(perms1.write.query(None), PermissionState::Granted);
- assert_eq!(perms1.write.query(Some(Path::new("/foo"))), PermissionState::Granted);
+ assert_eq!(perms1.write.query(Some(&write_query("/foo"))), PermissionState::Granted);
assert_eq!(perms2.write.query(None), PermissionState::Prompt);
- assert_eq!(perms2.write.query(Some(Path::new("/foo"))), PermissionState::Granted);
- assert_eq!(perms2.write.query(Some(Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms2.write.query(Some(&write_query("/foo"))), PermissionState::Granted);
+ assert_eq!(perms2.write.query(Some(&write_query("/foo/bar"))), PermissionState::Granted);
assert_eq!(perms3.write.query(None), PermissionState::Prompt);
- assert_eq!(perms3.write.query(Some(Path::new("/foo"))), PermissionState::Denied);
- assert_eq!(perms3.write.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms3.write.query(Some(&write_query("/foo"))), PermissionState::Denied);
+ assert_eq!(perms3.write.query(Some(&write_query("/foo/bar"))), PermissionState::Denied);
assert_eq!(perms4.write.query(None), PermissionState::GrantedPartial);
- assert_eq!(perms4.write.query(Some(Path::new("/foo"))), PermissionState::Denied);
- assert_eq!(perms4.write.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
- assert_eq!(perms4.write.query(Some(Path::new("/bar"))), PermissionState::Granted);
+ assert_eq!(perms4.write.query(Some(&write_query("/foo"))), PermissionState::Denied);
+ assert_eq!(perms4.write.query(Some(&write_query("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms4.write.query(Some(&write_query("/bar"))), PermissionState::Granted);
assert_eq!(perms1.ffi.query(None), PermissionState::Granted);
- assert_eq!(perms1.ffi.query(Some(Path::new("/foo"))), PermissionState::Granted);
+ assert_eq!(perms1.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Granted);
assert_eq!(perms2.ffi.query(None), PermissionState::Prompt);
- assert_eq!(perms2.ffi.query(Some(Path::new("/foo"))), PermissionState::Granted);
- assert_eq!(perms2.ffi.query(Some(Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms2.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Granted);
+ assert_eq!(perms2.ffi.query(Some(&ffi_query("/foo/bar"))), PermissionState::Granted);
assert_eq!(perms3.ffi.query(None), PermissionState::Prompt);
- assert_eq!(perms3.ffi.query(Some(Path::new("/foo"))), PermissionState::Denied);
- assert_eq!(perms3.ffi.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms3.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Denied);
+ assert_eq!(perms3.ffi.query(Some(&ffi_query("/foo/bar"))), PermissionState::Denied);
assert_eq!(perms4.ffi.query(None), PermissionState::GrantedPartial);
- assert_eq!(perms4.ffi.query(Some(Path::new("/foo"))), PermissionState::Denied);
- assert_eq!(perms4.ffi.query(Some(Path::new("/foo/bar"))), PermissionState::Denied);
- assert_eq!(perms4.ffi.query(Some(Path::new("/bar"))), PermissionState::Granted);
+ assert_eq!(perms4.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Denied);
+ assert_eq!(perms4.ffi.query(Some(&ffi_query("/foo/bar"))), PermissionState::Denied);
+ assert_eq!(perms4.ffi.query(Some(&ffi_query("/bar"))), PermissionState::Granted);
assert_eq!(perms1.net.query(None), PermissionState::Granted);
assert_eq!(perms1.net.query(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), None))), PermissionState::Granted);
assert_eq!(perms2.net.query(None), PermissionState::Prompt);
@@ -2865,37 +3366,54 @@ mod tests {
assert_eq!(perms4.sys.query(Some("hostname")), PermissionState::Denied);
assert_eq!(perms4.sys.query(Some("uid")), PermissionState::Granted);
assert_eq!(perms1.run.query(None), PermissionState::Granted);
- assert_eq!(perms1.run.query(Some("deno")), PermissionState::Granted);
+ let deno_run_query = RunQueryDescriptor::Path {
+ requested: "deno".to_string(),
+ resolved: PathBuf::from("/deno"),
+ };
+ let node_run_query = RunQueryDescriptor::Path {
+ requested: "node".to_string(),
+ resolved: PathBuf::from("/node"),
+ };
+ assert_eq!(perms1.run.query(Some(&deno_run_query)), PermissionState::Granted);
+ assert_eq!(perms1.write.query(Some(&write_query("/deno"))), PermissionState::Granted);
assert_eq!(perms2.run.query(None), PermissionState::Prompt);
- assert_eq!(perms2.run.query(Some("deno")), PermissionState::Granted);
+ assert_eq!(perms2.run.query(Some(&deno_run_query)), PermissionState::Granted);
+ assert_eq!(perms2.write.query(Some(&write_query("/deno"))), PermissionState::Denied);
assert_eq!(perms3.run.query(None), PermissionState::Prompt);
- assert_eq!(perms3.run.query(Some("deno")), PermissionState::Denied);
+ assert_eq!(perms3.run.query(Some(&deno_run_query)), PermissionState::Denied);
assert_eq!(perms4.run.query(None), PermissionState::GrantedPartial);
- assert_eq!(perms4.run.query(Some("deno")), PermissionState::Denied);
- assert_eq!(perms4.run.query(Some("node")), PermissionState::Granted);
+ assert_eq!(perms4.run.query(Some(&deno_run_query)), PermissionState::Denied);
+ assert_eq!(perms4.run.query(Some(&node_run_query)), PermissionState::Granted);
};
}
#[test]
fn test_request() {
set_prompter(Box::new(TestPrompter));
+ let parser = TestPermissionDescriptorParser;
let mut perms: Permissions = Permissions::none_without_prompt();
+ let read_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_read();
+ let write_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_write();
+ let ffi_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
#[rustfmt::skip]
{
let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
prompt_value.set(true);
- assert_eq!(perms.read.request(Some(Path::new("/foo"))), PermissionState::Granted);
+ assert_eq!(perms.read.request(Some(&read_query("/foo"))), PermissionState::Granted);
assert_eq!(perms.read.query(None), PermissionState::Prompt);
prompt_value.set(false);
- assert_eq!(perms.read.request(Some(Path::new("/foo/bar"))), PermissionState::Granted);
+ assert_eq!(perms.read.request(Some(&read_query("/foo/bar"))), PermissionState::Granted);
prompt_value.set(false);
- assert_eq!(perms.write.request(Some(Path::new("/foo"))), PermissionState::Denied);
- assert_eq!(perms.write.query(Some(Path::new("/foo/bar"))), PermissionState::Prompt);
+ assert_eq!(perms.write.request(Some(&write_query("/foo"))), PermissionState::Denied);
+ assert_eq!(perms.write.query(Some(&write_query("/foo/bar"))), PermissionState::Prompt);
prompt_value.set(true);
assert_eq!(perms.write.request(None), PermissionState::Denied);
prompt_value.set(false);
- assert_eq!(perms.ffi.request(Some(Path::new("/foo"))), PermissionState::Denied);
- assert_eq!(perms.ffi.query(Some(Path::new("/foo/bar"))), PermissionState::Prompt);
+ assert_eq!(perms.ffi.request(Some(&ffi_query("/foo"))), PermissionState::Denied);
+ assert_eq!(perms.ffi.query(Some(&ffi_query("/foo/bar"))), PermissionState::Prompt);
prompt_value.set(true);
assert_eq!(perms.ffi.request(None), PermissionState::Denied);
prompt_value.set(true);
@@ -2913,69 +3431,62 @@ mod tests {
prompt_value.set(false);
assert_eq!(perms.sys.request(Some("hostname")), PermissionState::Granted);
prompt_value.set(true);
- assert_eq!(perms.run.request(Some("deno")), PermissionState::Granted);
+ let run_query = RunQueryDescriptor::Path {
+ requested: "deno".to_string(),
+ resolved: PathBuf::from("/deno"),
+ };
+ assert_eq!(perms.run.request(Some(&run_query)), PermissionState::Granted);
assert_eq!(perms.run.query(None), PermissionState::Prompt);
prompt_value.set(false);
- assert_eq!(perms.run.request(Some("deno")), PermissionState::Granted);
+ assert_eq!(perms.run.request(Some(&run_query)), PermissionState::Granted);
};
}
#[test]
fn test_revoke() {
set_prompter(Box::new(TestPrompter));
- let mut perms = Permissions {
- read: Permissions::new_unary(
- Some(&[PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
- None,
- false,
- )
- .unwrap(),
- write: Permissions::new_unary(
- Some(&[PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
- None,
- false,
- )
- .unwrap(),
- ffi: Permissions::new_unary(
- Some(&[PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
- None,
- false,
- )
- .unwrap(),
- net: Permissions::new_unary(
- Some(&sarr!["127.0.0.1", "127.0.0.1:8000"]),
- None,
- false,
- )
- .unwrap(),
- env: Permissions::new_unary(Some(&sarr!["HOME"]), None, false).unwrap(),
- sys: Permissions::new_unary(Some(&sarr!["hostname"]), None, false)
- .unwrap(),
- run: Permissions::new_unary(
- Some(&["deno".to_string().into()]),
- None,
- false,
- )
- .unwrap(),
- all: Permissions::new_all(false),
- };
+ let parser = TestPermissionDescriptorParser;
+ let mut perms = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_read: Some(svec!["/foo", "/foo/baz"]),
+ allow_write: Some(svec!["/foo", "/foo/baz"]),
+ allow_ffi: Some(svec!["/foo", "/foo/baz"]),
+ allow_net: Some(svec!["127.0.0.1", "127.0.0.1:8000"]),
+ allow_env: Some(svec!["HOME"]),
+ allow_sys: Some(svec!["hostname"]),
+ allow_run: Some(svec!["/deno"]),
+ ..Default::default()
+ },
+ )
+ .unwrap();
+ let read_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_read();
+ let write_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_write();
+ let ffi_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
#[rustfmt::skip]
{
- assert_eq!(perms.read.revoke(Some(Path::new("/foo/bar"))), PermissionState::Prompt);
- assert_eq!(perms.read.query(Some(Path::new("/foo"))), PermissionState::Prompt);
- assert_eq!(perms.read.query(Some(Path::new("/foo/baz"))), PermissionState::Granted);
- assert_eq!(perms.write.revoke(Some(Path::new("/foo/bar"))), PermissionState::Prompt);
- assert_eq!(perms.write.query(Some(Path::new("/foo"))), PermissionState::Prompt);
- assert_eq!(perms.write.query(Some(Path::new("/foo/baz"))), PermissionState::Granted);
- assert_eq!(perms.ffi.revoke(Some(Path::new("/foo/bar"))), PermissionState::Prompt);
- assert_eq!(perms.ffi.query(Some(Path::new("/foo"))), PermissionState::Prompt);
- assert_eq!(perms.ffi.query(Some(Path::new("/foo/baz"))), PermissionState::Granted);
+ assert_eq!(perms.read.revoke(Some(&read_query("/foo/bar"))), PermissionState::Prompt);
+ assert_eq!(perms.read.query(Some(&read_query("/foo"))), PermissionState::Prompt);
+ assert_eq!(perms.read.query(Some(&read_query("/foo/baz"))), PermissionState::Granted);
+ assert_eq!(perms.write.revoke(Some(&write_query("/foo/bar"))), PermissionState::Prompt);
+ assert_eq!(perms.write.query(Some(&write_query("/foo"))), PermissionState::Prompt);
+ assert_eq!(perms.write.query(Some(&write_query("/foo/baz"))), PermissionState::Granted);
+ assert_eq!(perms.ffi.revoke(Some(&ffi_query("/foo/bar"))), PermissionState::Prompt);
+ assert_eq!(perms.ffi.query(Some(&ffi_query("/foo"))), PermissionState::Prompt);
+ assert_eq!(perms.ffi.query(Some(&ffi_query("/foo/baz"))), PermissionState::Granted);
assert_eq!(perms.net.revoke(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), Some(9000)))), PermissionState::Prompt);
assert_eq!(perms.net.query(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), None))), PermissionState::Prompt);
assert_eq!(perms.net.query(Some(&NetDescriptor(Host::must_parse("127.0.0.1"), Some(8000)))), PermissionState::Granted);
assert_eq!(perms.env.revoke(Some("HOME")), PermissionState::Prompt);
assert_eq!(perms.env.revoke(Some("hostname")), PermissionState::Prompt);
- assert_eq!(perms.run.revoke(Some("deno")), PermissionState::Prompt);
+ let run_query = RunQueryDescriptor::Path {
+ requested: "deno".to_string(),
+ resolved: PathBuf::from("/deno"),
+ };
+ assert_eq!(perms.run.revoke(Some(&run_query)), PermissionState::Prompt);
};
}
@@ -2984,24 +3495,31 @@ mod tests {
set_prompter(Box::new(TestPrompter));
let mut perms = Permissions::none_with_prompt();
let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
+ let parser = TestPermissionDescriptorParser;
+ let read_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_read();
+ let write_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_write();
+ let ffi_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
prompt_value.set(true);
- assert!(perms.read.check(Path::new("/foo"), None).is_ok());
+ assert!(perms.read.check(&read_query("/foo"), None).is_ok());
prompt_value.set(false);
- assert!(perms.read.check(Path::new("/foo"), None).is_ok());
- assert!(perms.read.check(Path::new("/bar"), None).is_err());
+ assert!(perms.read.check(&read_query("/foo"), None).is_ok());
+ assert!(perms.read.check(&read_query("/bar"), None).is_err());
prompt_value.set(true);
- assert!(perms.write.check(Path::new("/foo"), None).is_ok());
+ assert!(perms.write.check(&write_query("/foo"), None).is_ok());
prompt_value.set(false);
- assert!(perms.write.check(Path::new("/foo"), None).is_ok());
- assert!(perms.write.check(Path::new("/bar"), None).is_err());
+ assert!(perms.write.check(&write_query("/foo"), None).is_ok());
+ assert!(perms.write.check(&write_query("/bar"), None).is_err());
prompt_value.set(true);
- assert!(perms.ffi.check(Path::new("/foo"), None).is_ok());
+ assert!(perms.ffi.check(&ffi_query("/foo"), None).is_ok());
prompt_value.set(false);
- assert!(perms.ffi.check(Path::new("/foo"), None).is_ok());
- assert!(perms.ffi.check(Path::new("/bar"), None).is_err());
+ assert!(perms.ffi.check(&ffi_query("/foo"), None).is_ok());
+ assert!(perms.ffi.check(&ffi_query("/bar"), None).is_err());
prompt_value.set(true);
assert!(perms
@@ -3048,9 +3566,9 @@ mod tests {
assert!(perms
.run
.check(
- RunPathQuery {
- requested: "cat",
- resolved: &cwd.join("cat")
+ &RunQueryDescriptor::Path {
+ requested: "cat".to_string(),
+ resolved: cwd.join("cat")
},
None
)
@@ -3059,9 +3577,9 @@ mod tests {
assert!(perms
.run
.check(
- RunPathQuery {
- requested: "cat",
- resolved: &cwd.join("cat")
+ &RunQueryDescriptor::Path {
+ requested: "cat".to_string(),
+ resolved: cwd.join("cat")
},
None
)
@@ -3069,9 +3587,9 @@ mod tests {
assert!(perms
.run
.check(
- RunPathQuery {
- requested: "ls",
- resolved: &cwd.join("ls")
+ &RunQueryDescriptor::Path {
+ requested: "ls".to_string(),
+ resolved: cwd.join("ls")
},
None
)
@@ -3095,30 +3613,37 @@ mod tests {
set_prompter(Box::new(TestPrompter));
let mut perms = Permissions::none_with_prompt();
let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
+ let parser = TestPermissionDescriptorParser;
+ let read_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_read();
+ let write_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_write();
+ let ffi_query =
+ |path: &str| parser.parse_path_query(path).unwrap().into_ffi();
prompt_value.set(false);
- assert!(perms.read.check(Path::new("/foo"), None).is_err());
+ assert!(perms.read.check(&read_query("/foo"), None).is_err());
prompt_value.set(true);
- assert!(perms.read.check(Path::new("/foo"), None).is_err());
- assert!(perms.read.check(Path::new("/bar"), None).is_ok());
+ assert!(perms.read.check(&read_query("/foo"), None).is_err());
+ assert!(perms.read.check(&read_query("/bar"), None).is_ok());
prompt_value.set(false);
- assert!(perms.read.check(Path::new("/bar"), None).is_ok());
+ assert!(perms.read.check(&read_query("/bar"), None).is_ok());
prompt_value.set(false);
- assert!(perms.write.check(Path::new("/foo"), None).is_err());
+ assert!(perms.write.check(&write_query("/foo"), None).is_err());
prompt_value.set(true);
- assert!(perms.write.check(Path::new("/foo"), None).is_err());
- assert!(perms.write.check(Path::new("/bar"), None).is_ok());
+ assert!(perms.write.check(&write_query("/foo"), None).is_err());
+ assert!(perms.write.check(&write_query("/bar"), None).is_ok());
prompt_value.set(false);
- assert!(perms.write.check(Path::new("/bar"), None).is_ok());
+ assert!(perms.write.check(&write_query("/bar"), None).is_ok());
prompt_value.set(false);
- assert!(perms.ffi.check(Path::new("/foo"), None).is_err());
+ assert!(perms.ffi.check(&ffi_query("/foo"), None).is_err());
prompt_value.set(true);
- assert!(perms.ffi.check(Path::new("/foo"), None).is_err());
- assert!(perms.ffi.check(Path::new("/bar"), None).is_ok());
+ assert!(perms.ffi.check(&ffi_query("/foo"), None).is_err());
+ assert!(perms.ffi.check(&ffi_query("/bar"), None).is_ok());
prompt_value.set(false);
- assert!(perms.ffi.check(Path::new("/bar"), None).is_ok());
+ assert!(perms.ffi.check(&ffi_query("/bar"), None).is_ok());
prompt_value.set(false);
assert!(perms
@@ -3172,9 +3697,9 @@ mod tests {
assert!(perms
.run
.check(
- RunPathQuery {
- requested: "cat",
- resolved: &cwd.join("cat")
+ &RunQueryDescriptor::Path {
+ requested: "cat".to_string(),
+ resolved: cwd.join("cat")
},
None
)
@@ -3183,9 +3708,9 @@ mod tests {
assert!(perms
.run
.check(
- RunPathQuery {
- requested: "cat",
- resolved: &cwd.join("cat")
+ &RunQueryDescriptor::Path {
+ requested: "cat".to_string(),
+ resolved: cwd.join("cat")
},
None
)
@@ -3193,9 +3718,9 @@ mod tests {
assert!(perms
.run
.check(
- RunPathQuery {
- requested: "ls",
- resolved: &cwd.join("ls")
+ &RunQueryDescriptor::Path {
+ requested: "ls".to_string(),
+ resolved: cwd.join("ls")
},
None
)
@@ -3204,9 +3729,9 @@ mod tests {
assert!(perms
.run
.check(
- RunPathQuery {
- requested: "ls",
- resolved: &cwd.join("ls")
+ &RunQueryDescriptor::Path {
+ requested: "ls".to_string(),
+ resolved: cwd.join("ls")
},
None
)
@@ -3237,7 +3762,12 @@ mod tests {
let mut perms = Permissions::allow_all();
perms.env = UnaryPermission {
granted_global: false,
- ..Permissions::new_unary(Some(&sarr!["HOME"]), None, false).unwrap()
+ ..Permissions::new_unary(
+ Some(HashSet::from([EnvDescriptor::new("HOME")])),
+ None,
+ false,
+ )
+ .unwrap()
};
prompt_value.set(true);
@@ -3251,63 +3781,52 @@ mod tests {
#[test]
fn test_check_partial_denied() {
- let mut perms = Permissions {
- read: Permissions::new_unary(
- Some(&[]),
- Some(&[PathBuf::from("/foo/bar")]),
- false,
- )
- .unwrap(),
- write: Permissions::new_unary(
- Some(&[]),
- Some(&[PathBuf::from("/foo/bar")]),
- false,
- )
- .unwrap(),
- ..Permissions::none_without_prompt()
- };
+ let parser = TestPermissionDescriptorParser;
+ let mut perms = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_read: Some(vec![]),
+ deny_read: Some(svec!["/foo/bar"]),
+ allow_write: Some(vec![]),
+ deny_write: Some(svec!["/foo/bar"]),
+ ..Default::default()
+ },
+ )
+ .unwrap();
- perms.read.check_partial(Path::new("/foo"), None).unwrap();
- assert!(perms.read.check(Path::new("/foo"), None).is_err());
+ let read_query = parser.parse_path_query("/foo").unwrap().into_read();
+ perms.read.check_partial(&read_query, None).unwrap();
+ assert!(perms.read.check(&read_query, None).is_err());
- perms.write.check_partial(Path::new("/foo"), None).unwrap();
- assert!(perms.write.check(Path::new("/foo"), None).is_err());
+ let write_query = parser.parse_path_query("/foo").unwrap().into_write();
+ perms.write.check_partial(&write_query, None).unwrap();
+ assert!(perms.write.check(&write_query, None).is_err());
}
#[test]
fn test_net_fully_qualified_domain_name() {
- let mut perms = Permissions {
- net: Permissions::new_unary(
- Some(&["allowed.domain".to_string(), "1.1.1.1".to_string()]),
- Some(&["denied.domain".to_string(), "2.2.2.2".to_string()]),
- false,
- )
- .unwrap(),
- ..Permissions::none_without_prompt()
- };
+ set_prompter(Box::new(TestPrompter));
+ let parser = TestPermissionDescriptorParser;
+ let perms = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_net: Some(svec!["allowed.domain", "1.1.1.1"]),
+ deny_net: Some(svec!["denied.domain", "2.2.2.2"]),
+ ..Default::default()
+ },
+ )
+ .unwrap();
+ let mut perms = PermissionsContainer::new(Arc::new(parser), perms);
+ let cases = [
+ ("allowed.domain.", true),
+ ("1.1.1.1", true),
+ ("denied.domain.", false),
+ ("2.2.2.2", false),
+ ];
- perms
- .net
- .check(
- &NetDescriptor(Host::must_parse("allowed.domain."), None),
- None,
- )
- .unwrap();
- perms
- .net
- .check(&NetDescriptor(Host::must_parse("1.1.1.1"), None), None)
- .unwrap();
- assert!(perms
- .net
- .check(
- &NetDescriptor(Host::must_parse("denied.domain."), None),
- None
- )
- .is_err());
- assert!(perms
- .net
- .check(&NetDescriptor(Host::must_parse("2.2.2.2"), None), None)
- .is_err());
+ for (host, is_ok) in cases {
+ assert_eq!(perms.check_net(&(host, None), "api").is_ok(), is_ok);
+ }
}
#[test]
@@ -3443,14 +3962,19 @@ mod tests {
#[test]
fn test_create_child_permissions() {
set_prompter(Box::new(TestPrompter));
- let mut main_perms = Permissions {
- env: Permissions::new_unary(Some(&[]), None, false).unwrap(),
- net: Permissions::new_unary(Some(&sarr!["foo", "bar"]), None, false)
- .unwrap(),
- ..Permissions::none_without_prompt()
- };
+ let parser = TestPermissionDescriptorParser;
+ let mut main_perms = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ allow_env: Some(vec![]),
+ allow_net: Some(svec!["foo", "bar"]),
+ ..Default::default()
+ },
+ )
+ .unwrap();
assert_eq!(
create_child_permissions(
+ &parser,
&mut main_perms.clone(),
ChildPermissionsArg {
env: ChildUnaryPermissionArg::Inherit,
@@ -3461,12 +3985,18 @@ mod tests {
)
.unwrap(),
Permissions {
- env: Permissions::new_unary(Some(&[]), None, false).unwrap(),
- net: Permissions::new_unary(Some(&sarr!["foo"]), None, false).unwrap(),
+ env: Permissions::new_unary(Some(HashSet::new()), None, false).unwrap(),
+ net: Permissions::new_unary(
+ Some(HashSet::from([NetDescriptor::parse("foo").unwrap()])),
+ None,
+ false
+ )
+ .unwrap(),
..Permissions::none_without_prompt()
}
);
assert!(create_child_permissions(
+ &parser,
&mut main_perms.clone(),
ChildPermissionsArg {
net: ChildUnaryPermissionArg::Granted,
@@ -3475,6 +4005,7 @@ mod tests {
)
.is_err());
assert!(create_child_permissions(
+ &parser,
&mut main_perms.clone(),
ChildPermissionsArg {
net: ChildUnaryPermissionArg::GrantedList(svec!["foo", "bar", "baz"]),
@@ -3483,6 +4014,7 @@ mod tests {
)
.is_err());
assert!(create_child_permissions(
+ &parser,
&mut main_perms,
ChildPermissionsArg {
ffi: ChildUnaryPermissionArg::GrantedList(svec!["foo"]),
@@ -3496,13 +4028,17 @@ mod tests {
fn test_create_child_permissions_with_prompt() {
set_prompter(Box::new(TestPrompter));
let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
- let mut main_perms = Permissions::from_options(&PermissionsOptions {
- prompt: true,
- ..Default::default()
- })
+ let mut main_perms = Permissions::from_options(
+ &TestPermissionDescriptorParser,
+ &PermissionsOptions {
+ prompt: true,
+ ..Default::default()
+ },
+ )
.unwrap();
prompt_value.set(true);
let worker_perms = create_child_permissions(
+ &TestPermissionDescriptorParser,
&mut main_perms,
ChildPermissionsArg {
read: ChildUnaryPermissionArg::Granted,
@@ -3515,8 +4051,8 @@ mod tests {
assert_eq!(
main_perms.run.granted_list,
HashSet::from([
- RunDescriptor::Name("bar".to_owned()),
- RunDescriptor::Name("foo".to_owned())
+ AllowRunDescriptor(PathBuf::from("/bar")),
+ AllowRunDescriptor(PathBuf::from("/foo")),
])
);
}
@@ -3525,14 +4061,22 @@ mod tests {
fn test_create_child_permissions_with_inherited_denied_list() {
set_prompter(Box::new(TestPrompter));
let prompt_value = PERMISSION_PROMPT_STUB_VALUE_SETTER.lock();
- let mut main_perms = Permissions::from_options(&PermissionsOptions {
- prompt: true,
- ..Default::default()
- })
+ let parser = TestPermissionDescriptorParser;
+ let mut main_perms = Permissions::from_options(
+ &parser,
+ &PermissionsOptions {
+ prompt: true,
+ ..Default::default()
+ },
+ )
.unwrap();
prompt_value.set(false);
- assert!(main_perms.write.check(&PathBuf::from("foo"), None).is_err());
+ assert!(main_perms
+ .write
+ .check(&parser.parse_path_query("foo").unwrap().into_write(), None)
+ .is_err());
let worker_perms = create_child_permissions(
+ &TestPermissionDescriptorParser,
&mut main_perms.clone(),
ChildPermissionsArg::none(),
)
@@ -3544,30 +4088,6 @@ mod tests {
}
#[test]
- fn test_handle_empty_value() {
- set_prompter(Box::new(TestPrompter));
-
- assert!(Permissions::new_unary::<ReadDescriptor>(
- Some(&[Default::default()]),
- None,
- false
- )
- .is_err());
- assert!(Permissions::new_unary::<EnvDescriptor>(
- Some(&[Default::default()]),
- None,
- false
- )
- .is_err());
- assert!(Permissions::new_unary::<NetDescriptor>(
- Some(&[Default::default()]),
- None,
- false
- )
- .is_err());
- }
-
- #[test]
fn test_host_parse() {
let hosts = &[
("deno.land", Some(Host::Fqdn(fqdn!("deno.land")))),
@@ -3671,4 +4191,35 @@ mod tests {
assert_eq!(NetDescriptor::parse(input).ok(), *expected, "'{input}'");
}
}
+
+ #[test]
+ fn test_denies_run_name() {
+ let cases = [
+ #[cfg(windows)]
+ ("deno", "C:\\deno.exe", true),
+ #[cfg(windows)]
+ ("deno", "C:\\sub\\deno.cmd", true),
+ #[cfg(windows)]
+ ("deno", "C:\\sub\\DeNO.cmd", true),
+ #[cfg(windows)]
+ ("DEno", "C:\\sub\\deno.cmd", true),
+ #[cfg(windows)]
+ ("deno", "C:\\other\\sub\\deno.batch", true),
+ #[cfg(windows)]
+ ("deno", "C:\\other\\sub\\deno", true),
+ #[cfg(windows)]
+ ("denort", "C:\\other\\sub\\deno.exe", false),
+ ("deno", "/home/test/deno", true),
+ ("deno", "/home/test/denot", false),
+ ];
+ for (name, cmd_path, denies) in cases {
+ assert_eq!(
+ denies_run_name(name, &PathBuf::from(cmd_path)),
+ denies,
+ "{} {}",
+ name,
+ cmd_path
+ );
+ }
+ }
}
diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs
index fd422603f..db6688b46 100644
--- a/runtime/snapshot.rs
+++ b/runtime/snapshot.rs
@@ -2,6 +2,7 @@
use crate::ops;
use crate::ops::bootstrap::SnapshotOptions;
+use crate::permissions::RuntimePermissionDescriptorParser;
use crate::shared::maybe_transpile_source;
use crate::shared::runtime;
use deno_cache::SqliteBackedCache;
@@ -11,6 +12,7 @@ use deno_core::v8;
use deno_core::Extension;
use deno_http::DefaultHttpPropertyExtractor;
use deno_io::fs::FsError;
+use std::borrow::Cow;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
@@ -45,29 +47,32 @@ impl deno_fetch::FetchPermissions for Permissions {
unreachable!("snapshotting!")
}
- fn check_read(
+ fn check_read<'a>(
&mut self,
- _p: &Path,
+ _p: &'a Path,
_api_name: &str,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<Cow<'a, Path>, AnyError> {
unreachable!("snapshotting!")
}
}
impl deno_ffi::FfiPermissions for Permissions {
- fn check_partial(
+ fn check_partial_no_path(
&mut self,
- _path: Option<&Path>,
) -> Result<(), deno_core::error::AnyError> {
unreachable!("snapshotting!")
}
+
+ fn check_partial_with_path(
+ &mut self,
+ _path: &str,
+ ) -> Result<PathBuf, AnyError> {
+ unreachable!("snapshotting!")
+ }
}
impl deno_napi::NapiPermissions for Permissions {
- fn check(
- &mut self,
- _path: Option<&Path>,
- ) -> Result<(), deno_core::error::AnyError> {
+ fn check(&mut self, _path: &str) -> std::result::Result<PathBuf, AnyError> {
unreachable!("snapshotting!")
}
}
@@ -80,18 +85,24 @@ impl deno_node::NodePermissions for Permissions {
) -> Result<(), deno_core::error::AnyError> {
unreachable!("snapshotting!")
}
+ fn check_read_path<'a>(
+ &mut self,
+ _path: &'a Path,
+ ) -> Result<Cow<'a, Path>, AnyError> {
+ unreachable!("snapshotting!")
+ }
fn check_read_with_api_name(
&mut self,
- _p: &Path,
+ _p: &str,
_api_name: Option<&str>,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<PathBuf, deno_core::error::AnyError> {
unreachable!("snapshotting!")
}
fn check_write_with_api_name(
&mut self,
- _p: &Path,
+ _p: &str,
_api_name: Option<&str>,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<PathBuf, deno_core::error::AnyError> {
unreachable!("snapshotting!")
}
fn check_sys(
@@ -114,17 +125,25 @@ impl deno_net::NetPermissions for Permissions {
fn check_read(
&mut self,
- _p: &Path,
+ _p: &str,
_api_name: &str,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<PathBuf, deno_core::error::AnyError> {
unreachable!("snapshotting!")
}
fn check_write(
&mut self,
- _p: &Path,
+ _p: &str,
_api_name: &str,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<PathBuf, deno_core::error::AnyError> {
+ unreachable!("snapshotting!")
+ }
+
+ fn check_write_path<'a>(
+ &mut self,
+ _p: &'a Path,
+ _api_name: &str,
+ ) -> Result<std::borrow::Cow<'a, Path>, AnyError> {
unreachable!("snapshotting!")
}
}
@@ -143,9 +162,9 @@ impl deno_fs::FsPermissions for Permissions {
fn check_read(
&mut self,
- _path: &Path,
+ _path: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
unreachable!("snapshotting!")
}
@@ -164,17 +183,17 @@ impl deno_fs::FsPermissions for Permissions {
fn check_write(
&mut self,
- _path: &Path,
+ _path: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
unreachable!("snapshotting!")
}
fn check_write_partial(
&mut self,
- _path: &Path,
+ _path: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
unreachable!("snapshotting!")
}
@@ -190,22 +209,38 @@ impl deno_fs::FsPermissions for Permissions {
) -> Result<(), AnyError> {
unreachable!("snapshotting!")
}
+
+ fn check_read_path<'a>(
+ &mut self,
+ _path: &'a Path,
+ _api_name: &str,
+ ) -> Result<std::borrow::Cow<'a, Path>, AnyError> {
+ unreachable!("snapshotting!")
+ }
+
+ fn check_write_path<'a>(
+ &mut self,
+ _path: &'a Path,
+ _api_name: &str,
+ ) -> Result<std::borrow::Cow<'a, Path>, AnyError> {
+ unreachable!("snapshotting!")
+ }
}
impl deno_kv::sqlite::SqliteDbHandlerPermissions for Permissions {
fn check_read(
&mut self,
- _path: &Path,
+ _path: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<PathBuf, AnyError> {
unreachable!("snapshotting!")
}
- fn check_write(
+ fn check_write<'a>(
&mut self,
- _path: &Path,
+ _path: &'a Path,
_api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<Cow<'a, Path>, AnyError> {
unreachable!("snapshotting!")
}
}
@@ -255,7 +290,7 @@ pub fn create_runtime_snapshot(
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
deno_io::deno_io::init_ops_and_esm(Default::default()),
deno_fs::deno_fs::init_ops_and_esm::<Permissions>(fs.clone()),
- deno_node::deno_node::init_ops_and_esm::<Permissions>(None, fs),
+ deno_node::deno_node::init_ops_and_esm::<Permissions>(None, fs.clone()),
runtime::init_ops_and_esm(),
ops::runtime::deno_runtime::init_ops("deno:runtime".parse().unwrap()),
ops::worker_host::deno_worker_host::init_ops(
@@ -264,7 +299,9 @@ pub fn create_runtime_snapshot(
),
ops::fs_events::deno_fs_events::init_ops(),
ops::os::deno_os::init_ops(Default::default()),
- ops::permissions::deno_permissions::init_ops(),
+ ops::permissions::deno_permissions::init_ops(Arc::new(
+ RuntimePermissionDescriptorParser::new(fs),
+ )),
ops::process::deno_process::init_ops(),
ops::signal::deno_signal::init_ops(),
ops::tty::deno_tty::init_ops(),
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index e14328861..7b69a9798 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -44,6 +44,7 @@ use deno_http::DefaultHttpPropertyExtractor;
use deno_io::Stdio;
use deno_kv::dynamic::MultiBackendDbHandler;
use deno_node::NodeExtInitServices;
+use deno_permissions::PermissionDescriptorParser;
use deno_permissions::PermissionsContainer;
use deno_terminal::colors;
use deno_tls::RootCertStoreProvider;
@@ -356,6 +357,7 @@ pub struct WebWorker {
}
pub struct WebWorkerOptions {
+ // todo(dsherret): extract out the service structs from this options bag
pub bootstrap: BootstrapOptions,
pub extensions: Vec<Extension>,
pub startup_snapshot: Option<&'static [u8]>,
@@ -377,6 +379,7 @@ pub struct WebWorkerOptions {
pub cache_storage_dir: Option<std::path::PathBuf>,
pub stdio: Stdio,
pub feature_checker: Arc<FeatureChecker>,
+ pub permission_desc_parser: Arc<dyn PermissionDescriptorParser>,
pub strace_ops: Option<Vec<String>>,
pub close_on_idle: bool,
pub maybe_worker_metadata: Option<WorkerMetadata>,
@@ -501,7 +504,9 @@ impl WebWorker {
),
ops::fs_events::deno_fs_events::init_ops_and_esm(),
ops::os::deno_os_worker::init_ops_and_esm(),
- ops::permissions::deno_permissions::init_ops_and_esm(),
+ ops::permissions::deno_permissions::init_ops_and_esm(
+ options.permission_desc_parser.clone(),
+ ),
ops::process::deno_process::init_ops_and_esm(),
ops::signal::deno_signal::init_ops_and_esm(),
ops::tty::deno_tty::init_ops_and_esm(),
diff --git a/runtime/worker.rs b/runtime/worker.rs
index 02749e7c1..3d8c8a0b9 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -49,6 +49,7 @@ use crate::code_cache::CodeCache;
use crate::code_cache::CodeCacheType;
use crate::inspector_server::InspectorServer;
use crate::ops;
+use crate::permissions::RuntimePermissionDescriptorParser;
use crate::shared::maybe_transpile_source;
use crate::shared::runtime;
use crate::BootstrapOptions;
@@ -157,6 +158,8 @@ pub struct WorkerOptions {
/// executed tries to load modules.
pub module_loader: Rc<dyn ModuleLoader>,
pub node_services: Option<NodeExtInitServices>,
+ pub permission_desc_parser:
+ Arc<dyn deno_permissions::PermissionDescriptorParser>,
// Callbacks invoked when creating new instance of WebWorker
pub create_web_worker_cb: Arc<ops::worker_host::CreateWebWorkerCb>,
pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>,
@@ -201,13 +204,16 @@ pub struct WorkerOptions {
pub v8_code_cache: Option<Arc<dyn CodeCache>>,
}
+// todo(dsherret): this is error prone to use. We should separate
+// out the WorkerOptions from the services.
impl Default for WorkerOptions {
fn default() -> Self {
+ let real_fs = Arc::new(deno_fs::RealFs);
Self {
create_web_worker_cb: Arc::new(|_| {
unimplemented!("web workers are not supported")
}),
- fs: Arc::new(deno_fs::RealFs),
+ fs: real_fs.clone(),
module_loader: Rc::new(FsModuleLoader),
skip_op_registration: false,
seed: None,
@@ -232,6 +238,9 @@ impl Default for WorkerOptions {
bootstrap: Default::default(),
stdio: Default::default(),
feature_checker: Default::default(),
+ permission_desc_parser: Arc::new(RuntimePermissionDescriptorParser::new(
+ real_fs,
+ )),
v8_code_cache: Default::default(),
}
}
@@ -425,7 +434,9 @@ impl MainWorker {
),
ops::fs_events::deno_fs_events::init_ops_and_esm(),
ops::os::deno_os::init_ops_and_esm(exit_code.clone()),
- ops::permissions::deno_permissions::init_ops_and_esm(),
+ ops::permissions::deno_permissions::init_ops_and_esm(
+ options.permission_desc_parser,
+ ),
ops::process::deno_process::init_ops_and_esm(),
ops::signal::deno_signal::init_ops_and_esm(),
ops::tty::deno_tty::init_ops_and_esm(),
diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs
index 95b6e5a01..10a266fcc 100644
--- a/tests/integration/run_tests.rs
+++ b/tests/integration/run_tests.rs
@@ -255,12 +255,6 @@ itest!(_052_no_remote_flag {
http_server: true,
});
-itest!(_056_make_temp_file_write_perm {
- args:
- "run --quiet --allow-read --allow-write=./subdir/ run/056_make_temp_file_write_perm.ts",
- output: "run/056_make_temp_file_write_perm.out",
-});
-
itest!(_058_tasks_microtasks_close {
args: "run --quiet run/058_tasks_microtasks_close.ts",
output: "run/058_tasks_microtasks_close.ts.out",
diff --git a/tests/specs/permission/deny_run_binary_absolute_path/__test__.jsonc b/tests/specs/permission/deny_run_binary_absolute_path/__test__.jsonc
new file mode 100644
index 000000000..fac0d928a
--- /dev/null
+++ b/tests/specs/permission/deny_run_binary_absolute_path/__test__.jsonc
@@ -0,0 +1,8 @@
+{
+ "envs": {
+ "DYLD_FALLBACK_LIBRARY_PATH": "",
+ "LD_LIBRARY_PATH": ""
+ },
+ "args": "run --allow-run --deny-run=deno --allow-read main.ts",
+ "output": "main.out"
+}
diff --git a/tests/specs/permission/deny_run_binary_absolute_path/main.out b/tests/specs/permission/deny_run_binary_absolute_path/main.out
new file mode 100644
index 000000000..45b228387
--- /dev/null
+++ b/tests/specs/permission/deny_run_binary_absolute_path/main.out
@@ -0,0 +1,8 @@
+NotCapable: Requires run access to "deno", run again with the --allow-run flag
+ at [WILDCARD] {
+ name: "NotCapable"
+}
+NotCapable: Requires run access to "[WILDLINE]", run again with the --allow-run flag
+ at [WILDCARD] {
+ name: "NotCapable"
+}
diff --git a/tests/specs/permission/deny_run_binary_absolute_path/main.ts b/tests/specs/permission/deny_run_binary_absolute_path/main.ts
new file mode 100644
index 000000000..eca5e5a33
--- /dev/null
+++ b/tests/specs/permission/deny_run_binary_absolute_path/main.ts
@@ -0,0 +1,15 @@
+try {
+ new Deno.Command("deno", {
+ args: ["--version"],
+ }).outputSync();
+} catch (err) {
+ console.error(err);
+}
+
+try {
+ new Deno.Command(Deno.execPath(), {
+ args: ["--version"],
+ }).outputSync();
+} catch (err) {
+ console.error(err);
+}
diff --git a/tests/specs/permission/make_temp_write_perm/056_make_temp_file_write_perm.out b/tests/specs/permission/make_temp_write_perm/056_make_temp_file_write_perm.out
new file mode 100644
index 000000000..7144e088c
--- /dev/null
+++ b/tests/specs/permission/make_temp_write_perm/056_make_temp_file_write_perm.out
@@ -0,0 +1,4 @@
+good [WILDCARD]subdir[WILDCARD]
+good [WILDCARD]subdir[WILDCARD]
+good [WILDCARD]subdir[WILDCARD]
+good [WILDCARD]subdir[WILDCARD]
diff --git a/tests/specs/permission/make_temp_write_perm/056_make_temp_file_write_perm.ts b/tests/specs/permission/make_temp_write_perm/056_make_temp_file_write_perm.ts
new file mode 100644
index 000000000..28661973c
--- /dev/null
+++ b/tests/specs/permission/make_temp_write_perm/056_make_temp_file_write_perm.ts
@@ -0,0 +1,52 @@
+Deno.mkdirSync("subdir");
+
+// async file
+{
+ const path = await Deno.makeTempFile({ dir: `subdir` });
+ try {
+ if (!path.match(/^subdir[/\\][^/\\]+/)) {
+ throw Error("bad " + path);
+ }
+ console.log("good", path);
+ } finally {
+ await Deno.remove(path);
+ }
+}
+// sync file
+{
+ const path = Deno.makeTempFileSync({ dir: `subdir` });
+ try {
+ if (!path.match(/^subdir[/\\][^/\\]+/)) {
+ throw Error("bad " + path);
+ }
+ console.log("good", path);
+ } finally {
+ await Deno.remove(path);
+ }
+}
+
+// async dir
+{
+ const path = await Deno.makeTempDir({ dir: `subdir` });
+ try {
+ if (!path.match(/^subdir[/\\][^/\\]+/)) {
+ throw Error("bad " + path);
+ }
+ console.log("good", path);
+ } finally {
+ await Deno.remove(path);
+ }
+}
+
+// sync dir
+{
+ const path = Deno.makeTempDirSync({ dir: `subdir` });
+ try {
+ if (!path.match(/^subdir[/\\][^/\\]+/)) {
+ throw Error("bad " + path);
+ }
+ console.log("good", path);
+ } finally {
+ await Deno.remove(path);
+ }
+}
diff --git a/tests/specs/permission/make_temp_write_perm/__test__.jsonc b/tests/specs/permission/make_temp_write_perm/__test__.jsonc
new file mode 100644
index 000000000..80a503215
--- /dev/null
+++ b/tests/specs/permission/make_temp_write_perm/__test__.jsonc
@@ -0,0 +1,15 @@
+{
+ "tempDir": true,
+ "tests": {
+ "reduced_perms": {
+ // this should not expose the full directory
+ "args": "run --quiet --allow-read --allow-write=./subdir/ 056_make_temp_file_write_perm.ts",
+ "output": "056_make_temp_file_write_perm.out"
+ },
+ "all_perms": {
+ // this will work the same as above
+ "args": "run --quiet -A 056_make_temp_file_write_perm.ts",
+ "output": "056_make_temp_file_write_perm.out"
+ }
+ }
+}
diff --git a/tests/specs/run/allow_run_allowlist_resolution/__test__.jsonc b/tests/specs/run/allow_run_allowlist_resolution/__test__.jsonc
index 173e13027..3e5d86adf 100644
--- a/tests/specs/run/allow_run_allowlist_resolution/__test__.jsonc
+++ b/tests/specs/run/allow_run_allowlist_resolution/__test__.jsonc
@@ -1,8 +1,9 @@
{
- "args": "run --quiet -A main.ts",
- "output": "main.out",
+ "tempDir": true,
"envs": {
"DYLD_FALLBACK_LIBRARY_PATH": "",
"LD_LIBRARY_PATH": ""
- }
+ },
+ "args": "run --quiet -A main.ts",
+ "output": "main.out"
}
diff --git a/tests/specs/run/allow_run_allowlist_resolution/main.out b/tests/specs/run/allow_run_allowlist_resolution/main.out
index f61f9b550..b494bb52f 100644
--- a/tests/specs/run/allow_run_allowlist_resolution/main.out
+++ b/tests/specs/run/allow_run_allowlist_resolution/main.out
@@ -3,7 +3,7 @@ PermissionStatus { state: "granted", onchange: null }
PermissionStatus { state: "prompt", onchange: null }
PermissionStatus { state: "granted", onchange: null }
---
-Info Failed to resolve 'deno' for allow-run: cannot find binary path
+Info Failed to resolve 'binary' for allow-run: cannot find binary path
PermissionStatus { state: "prompt", onchange: null }
PermissionStatus { state: "prompt", onchange: null }
PermissionStatus { state: "prompt", onchange: null }
diff --git a/tests/specs/run/allow_run_allowlist_resolution/main.ts b/tests/specs/run/allow_run_allowlist_resolution/main.ts
index bf33d8cbe..e43e0b5da 100644
--- a/tests/specs/run/allow_run_allowlist_resolution/main.ts
+++ b/tests/specs/run/allow_run_allowlist_resolution/main.ts
@@ -1,36 +1,41 @@
-// Testing the following (but with `deno` instead of `echo`):
-// | `deno run --allow-run=echo` | `which path == "/usr/bin/echo"` at startup | `which path != "/usr/bin/echo"` at startup |
-// |-------------------------------------|--------------------------------------------|--------------------------------------------|
-// | **`Deno.Command("echo")`** | ✅ | ✅ |
-// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ❌ |
+// Testing the following:
+// | `deno run --allow-run=binary` | `which path == "/usr/bin/binary"` at startup | `which path != "/usr/bin/binary"` at startup |
+// |---------------------------------------|----------------------------------------------|--------------------------------------------|
+// | **`Deno.Command("binary")`** | :white_check_mark: | :white_check_mark: |
+// | **`Deno.Command("/usr/bin/binary")`** | :white_check_mark: | :x: |
+// | `deno run --allow-run=/usr/bin/binary | `which path == "/usr/bin/binary"` at runtime | `which path != "/usr/bin/binary"` at runtime |
+// |---------------------------------------|----------------------------------------------|--------------------------------------------|
+// | **`Deno.Command("binary")`** | :white_check_mark: | :x: |
+// | **`Deno.Command("/usr/bin/binary")`** | :white_check_mark: | :white_check_mark: |
-// | `deno run --allow-run=/usr/bin/echo | `which path == "/usr/bin/echo"` at runtime | `which path != "/usr/bin/echo"` at runtime |
-// |-------------------------------------|--------------------------------------------|--------------------------------------------|
-// | **`Deno.Command("echo")`** | ✅ | ❌ |
-// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ✅ |
+const binaryName = Deno.build.os === "windows" ? "binary.exe" : "binary";
+const pathSep = Deno.build.os === "windows" ? "\\" : "/";
+const cwd = Deno.cwd();
+const execPathParent = `${Deno.cwd()}${pathSep}sub`;
+const execPath = `${execPathParent}${pathSep}${binaryName}`;
-const execPath = Deno.execPath();
-const execPathParent = execPath.replace(/[/\\][^/\\]+$/, "");
+Deno.mkdirSync(execPathParent);
+Deno.copyFileSync(Deno.execPath(), execPath);
const testUrl = `data:application/typescript;base64,${
btoa(`
- console.error(await Deno.permissions.query({ name: "run", command: "deno" }));
+ console.error(await Deno.permissions.query({ name: "run", command: "binary" }));
console.error(await Deno.permissions.query({ name: "run", command: "${
execPath.replaceAll("\\", "\\\\")
}" }));
Deno.env.set("PATH", "");
- console.error(await Deno.permissions.query({ name: "run", command: "deno" }));
+ console.error(await Deno.permissions.query({ name: "run", command: "binary" }));
console.error(await Deno.permissions.query({ name: "run", command: "${
execPath.replaceAll("\\", "\\\\")
}" }));
`)
}`;
-const process1 = await new Deno.Command(Deno.execPath(), {
+await new Deno.Command(Deno.execPath(), {
args: [
"run",
"--allow-env",
- "--allow-run=deno",
+ "--allow-run=binary",
testUrl,
],
stdout: "inherit",
@@ -44,7 +49,7 @@ await new Deno.Command(Deno.execPath(), {
args: [
"run",
"--allow-env",
- "--allow-run=deno",
+ "--allow-run=binary",
testUrl,
],
stderr: "inherit",
diff --git a/tests/specs/run/ld_preload/__test__.jsonc b/tests/specs/run/ld_preload/__test__.jsonc
index 882f157e9..16ae697a7 100644
--- a/tests/specs/run/ld_preload/__test__.jsonc
+++ b/tests/specs/run/ld_preload/__test__.jsonc
@@ -6,11 +6,11 @@
},
"tests": {
"env_arg": {
- "args": "run --allow-run=echo env_arg.ts",
+ "args": "run --allow-run=curl env_arg.ts",
"output": "env_arg.out"
},
"set_with_allow_env": {
- "args": "run --allow-run=echo --allow-env set_with_allow_env.ts",
+ "args": "run --allow-run=curl --allow-env set_with_allow_env.ts",
"output": "set_with_allow_env.out"
}
}
diff --git a/tests/specs/run/ld_preload/set_with_allow_env.ts b/tests/specs/run/ld_preload/set_with_allow_env.ts
index 79004aa16..a3e8dd397 100644
--- a/tests/specs/run/ld_preload/set_with_allow_env.ts
+++ b/tests/specs/run/ld_preload/set_with_allow_env.ts
@@ -1,7 +1,7 @@
Deno.env.set("LD_PRELOAD", "./libpreload.so");
try {
- new Deno.Command("echo").spawn();
+ new Deno.Command("curl").spawn();
} catch (err) {
console.log(err);
}
@@ -9,7 +9,7 @@ try {
Deno.env.set("DYLD_FALLBACK_LIBRARY_PATH", "./libpreload.so");
try {
- Deno.run({ cmd: ["echo"] }).spawnSync();
+ Deno.run({ cmd: ["curl"] }).spawnSync();
} catch (err) {
console.log(err);
}
diff --git a/tests/testdata/run/056_make_temp_file_write_perm.out b/tests/testdata/run/056_make_temp_file_write_perm.out
deleted file mode 100644
index c56aae43f..000000000
--- a/tests/testdata/run/056_make_temp_file_write_perm.out
+++ /dev/null
@@ -1 +0,0 @@
-good [WILDCARD]subdir[WILDCARD]
diff --git a/tests/testdata/run/056_make_temp_file_write_perm.ts b/tests/testdata/run/056_make_temp_file_write_perm.ts
deleted file mode 100644
index c0deda8a2..000000000
--- a/tests/testdata/run/056_make_temp_file_write_perm.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-const path = await Deno.makeTempFile({ dir: `subdir` });
-try {
- if (!path.match(/^subdir[/\\][^/\\]+/)) {
- throw Error("bad " + path);
- }
- console.log("good", path);
-} finally {
- await Deno.remove(path);
-}
diff --git a/tests/testdata/workers/read_check_granular_worker.js b/tests/testdata/workers/read_check_granular_worker.js
index 7f2d0f717..01df8ca97 100644
--- a/tests/testdata/workers/read_check_granular_worker.js
+++ b/tests/testdata/workers/read_check_granular_worker.js
@@ -1,4 +1,16 @@
// deno-fmt-ignore-file
+import { toFileUrl } from "@std/path/to-file-url";
+
+function tryGetCwd() {
+ // will throw in one test but not the other
+ try {
+ return Deno.cwd()
+ } catch {
+ return import.meta.dirname;
+ }
+}
+
+const fooExePath = tryGetCwd() + "/foo" + (Deno.build.os === "windows" ? ".exe" : "");
postMessage({
envGlobal: (await Deno.permissions.query({ name: "env" })).state,
envFoo: (await Deno.permissions.query({ name: "env", variable: "foo" })).state,
@@ -15,11 +27,13 @@ postMessage({
readGlobal: (await Deno.permissions.query({ name: "read" })).state,
readFoo: (await Deno.permissions.query({ name: "read", path: new URL("foo", import.meta.url) })).state,
readBar: (await Deno.permissions.query({ name: "read", path: "bar" })).state,
- readAbsent: (await Deno.permissions.query({ name: "read", path: "absent" })).state,
+ readAbsent: (await Deno.permissions.query({ name: "read", path: "../absent" })).state,
runGlobal: (await Deno.permissions.query({ name: "run" })).state,
- runFoo: (await Deno.permissions.query({ name: "run", command: new URL("foo", import.meta.url) })).state,
+ runFoo: (await Deno.permissions.query({ name: "run", command: toFileUrl(fooExePath) })).state,
+ runFooPath: (await Deno.permissions.query({ name: "run", command: fooExePath })).state,
runBar: (await Deno.permissions.query({ name: "run", command: "bar" })).state,
runBaz: (await Deno.permissions.query({ name: "run", command: "./baz" })).state,
+ runUnresolved: (await Deno.permissions.query({ name: "run", command: "unresolved-exec" })).state,
runAbsent: (await Deno.permissions.query({ name: "run", command: "absent" })).state,
writeGlobal: (await Deno.permissions.query({ name: "write" })).state,
writeFoo: (await Deno.permissions.query({ name: "write", path: new URL("foo", import.meta.url) })).state,
diff --git a/tests/unit/os_test.ts b/tests/unit/os_test.ts
index 4f760ecf8..a70796505 100644
--- a/tests/unit/os_test.ts
+++ b/tests/unit/os_test.ts
@@ -79,7 +79,9 @@ Deno.test(
) => {
const src = `
console.log(
- ${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env.get(k))
+ ${
+ JSON.stringify(Object.keys(expectedEnv))
+ }.map(k => Deno.env.get(k) ?? null)
)`;
const { success, stdout } = await new Deno.Command(Deno.execPath(), {
args: ["eval", src],
diff --git a/tests/unit/worker_test.ts b/tests/unit/worker_test.ts
index 88c6ca4c6..42c257282 100644
--- a/tests/unit/worker_test.ts
+++ b/tests/unit/worker_test.ts
@@ -5,6 +5,7 @@
// Requires to be run with `--allow-net` flag
import { assert, assertEquals, assertMatch, assertThrows } from "@std/assert";
+import { toFileUrl } from "@std/path/to-file-url";
function resolveWorker(worker: string): string {
return import.meta.resolve(`../testdata/workers/${worker}`);
@@ -442,7 +443,31 @@ Deno.test("Worker limit children permissions", async function () {
worker.terminate();
});
+function setupReadCheckGranularWorkerTest() {
+ const tempDir = Deno.realPathSync(Deno.makeTempDirSync());
+ const initialPath = Deno.env.get("PATH")!;
+ const initialCwd = Deno.cwd();
+ Deno.chdir(tempDir);
+ const envSep = Deno.build.os === "windows" ? ";" : ":";
+ Deno.env.set("PATH", initialPath + envSep + tempDir);
+
+ // create executables that will be resolved when doing `which`
+ const ext = Deno.build.os === "windows" ? ".exe" : "";
+ Deno.copyFileSync(Deno.execPath(), tempDir + "/bar" + ext);
+
+ return {
+ tempDir,
+ runFooFilePath: tempDir + "/foo" + ext,
+ [Symbol.dispose]() {
+ Deno.removeSync(tempDir, { recursive: true });
+ Deno.env.set("PATH", initialPath);
+ Deno.chdir(initialCwd);
+ },
+ };
+}
+
Deno.test("Worker limit children permissions granularly", async function () {
+ const ctx = setupReadCheckGranularWorkerTest();
const workerUrl = resolveWorker("read_check_granular_worker.js");
const worker = new Worker(
workerUrl,
@@ -453,8 +478,13 @@ Deno.test("Worker limit children permissions granularly", async function () {
env: ["foo"],
net: ["foo", "bar:8000"],
ffi: [new URL("foo", workerUrl), "bar"],
- read: [new URL("foo", workerUrl), "bar"],
- run: [new URL("foo", workerUrl), "bar", "./baz"],
+ read: [new URL("foo", workerUrl), "bar", ctx.tempDir],
+ run: [
+ toFileUrl(ctx.runFooFilePath),
+ "bar",
+ "./baz",
+ "unresolved-exec",
+ ],
write: [new URL("foo", workerUrl), "bar"],
},
},
@@ -482,8 +512,10 @@ Deno.test("Worker limit children permissions granularly", async function () {
readAbsent: "prompt",
runGlobal: "prompt",
runFoo: "granted",
+ runFooPath: "granted",
runBar: "granted",
runBaz: "granted",
+ runUnresolved: "prompt", // unresolved binaries remain as "prompt"
runAbsent: "prompt",
writeGlobal: "prompt",
writeFoo: "granted",
@@ -494,6 +526,7 @@ Deno.test("Worker limit children permissions granularly", async function () {
});
Deno.test("Nested worker limit children permissions", async function () {
+ const _cleanup = setupReadCheckGranularWorkerTest();
/** This worker has permissions but doesn't grant them to its children */
const worker = new Worker(
resolveWorker("parent_read_check_worker.js"),
@@ -521,8 +554,10 @@ Deno.test("Nested worker limit children permissions", async function () {
readAbsent: "prompt",
runGlobal: "prompt",
runFoo: "prompt",
+ runFooPath: "prompt",
runBar: "prompt",
runBaz: "prompt",
+ runUnresolved: "prompt",
runAbsent: "prompt",
writeGlobal: "prompt",
writeFoo: "prompt",
diff --git a/tools/lint.js b/tools/lint.js
index ae7b8470b..a9f940006 100755
--- a/tools/lint.js
+++ b/tools/lint.js
@@ -218,7 +218,7 @@ async function ensureNoNewITests() {
"pm_tests.rs": 0,
"publish_tests.rs": 0,
"repl_tests.rs": 0,
- "run_tests.rs": 334,
+ "run_tests.rs": 333,
"shared_library_tests.rs": 0,
"task_tests.rs": 4,
"test_tests.rs": 0,