1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-30 16:40:57 -05:00

refactor(permissions): split up Descriptor into Allow, Deny, and Query (#25508)

This makes the permission system more versatile.
This commit is contained in:
David Sherret 2024-09-16 21:39:37 +01:00 committed by GitHub
parent e0b9c745c1
commit 62e952559f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
74 changed files with 3062 additions and 1891 deletions

View file

@ -32,7 +32,6 @@ use deno_core::normalize_path;
use deno_core::resolve_url_or_path; use deno_core::resolve_url_or_path;
use deno_core::url::Url; use deno_core::url::Url;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_runtime::colors;
use deno_runtime::deno_permissions::parse_sys_kind; use deno_runtime::deno_permissions::parse_sys_kind;
use deno_runtime::deno_permissions::PermissionsOptions; use deno_runtime::deno_permissions::PermissionsOptions;
use log::debug; use log::debug;
@ -661,107 +660,25 @@ impl PermissionFlags {
|| self.deny_write.is_some() || self.deny_write.is_some()
} }
pub fn to_options( pub fn to_options(&self) -> PermissionsOptions {
&self, PermissionsOptions {
// 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 {
allow_all: self.allow_all, allow_all: self.allow_all,
allow_env: self.allow_env.clone(), allow_env: self.allow_env.clone(),
deny_env: self.deny_env.clone(), deny_env: self.deny_env.clone(),
allow_net: self.allow_net.clone(), allow_net: self.allow_net.clone(),
deny_net: self.deny_net.clone(), deny_net: self.deny_net.clone(),
allow_ffi: convert_option_str_to_path_buf(&self.allow_ffi, initial_cwd)?, allow_ffi: self.allow_ffi.clone(),
deny_ffi: convert_option_str_to_path_buf(&self.deny_ffi, initial_cwd)?, deny_ffi: self.deny_ffi.clone(),
allow_read: convert_option_str_to_path_buf( allow_read: self.allow_read.clone(),
&self.allow_read, deny_read: self.deny_read.clone(),
initial_cwd, allow_run: self.allow_run.clone(),
)?,
deny_read: convert_option_str_to_path_buf(&self.deny_read, initial_cwd)?,
allow_run,
deny_run: self.deny_run.clone(), deny_run: self.deny_run.clone(),
allow_sys: self.allow_sys.clone(), allow_sys: self.allow_sys.clone(),
deny_sys: self.deny_sys.clone(), deny_sys: self.deny_sys.clone(),
allow_write: convert_option_str_to_path_buf( allow_write: self.allow_write.clone(),
&self.allow_write, deny_write: self.deny_write.clone(),
initial_cwd,
)?,
deny_write,
prompt: !resolve_no_prompt(self), prompt: !resolve_no_prompt(self),
}) }
} }
} }

View file

@ -3,7 +3,6 @@
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::url::Url; use deno_core::url::Url;
use deno_runtime::deno_permissions::PermissionsContainer;
use crate::file_fetcher::FileFetcher; 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)?) Ok(serde_json::from_str(&data_url_text)?)
} else { } else {
let file = file_fetcher let file = file_fetcher
.fetch(specifier, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(specifier)
.await? .await?
.into_text_decoded()?; .into_text_decoded()?;
Ok(serde_json::from_str(&file.source)?) Ok(serde_json::from_str(&file.source)?)

View file

@ -27,7 +27,6 @@ use deno_npm::npm_rc::NpmRc;
use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use import_map::resolve_import_map_value_from_specifier; use import_map::resolve_import_map_value_from_specifier;
@ -1082,7 +1081,7 @@ impl CliOptions {
let specifier = specifier.clone(); let specifier = specifier.clone();
async move { async move {
let file = file_fetcher let file = file_fetcher
.fetch(&specifier, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(&specifier)
.await? .await?
.into_text_decoded()?; .into_text_decoded()?;
Ok(file.source.to_string()) Ok(file.source.to_string())
@ -1501,8 +1500,8 @@ impl CliOptions {
&self.flags.permissions &self.flags.permissions
} }
pub fn permissions_options(&self) -> Result<PermissionsOptions, AnyError> { pub fn permissions_options(&self) -> PermissionsOptions {
self.flags.permissions.to_options(Some(&self.initial_cwd)) self.flags.permissions.to_options()
} }
pub fn reload_flag(&self) -> bool { pub fn reload_flag(&self) -> bool {

8
cli/cache/mod.rs vendored
View file

@ -4,6 +4,7 @@ use crate::args::CacheSetting;
use crate::errors::get_error_class_name; use crate::errors::get_error_class_name;
use crate::file_fetcher::FetchNoFollowOptions; use crate::file_fetcher::FetchNoFollowOptions;
use crate::file_fetcher::FetchOptions; use crate::file_fetcher::FetchOptions;
use crate::file_fetcher::FetchPermissionsOption;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::file_fetcher::FileOrRedirect; use crate::file_fetcher::FileOrRedirect;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
@ -18,7 +19,6 @@ use deno_graph::source::CacheInfo;
use deno_graph::source::LoadFuture; use deno_graph::source::LoadFuture;
use deno_graph::source::LoadResponse; use deno_graph::source::LoadResponse;
use deno_graph::source::Loader; use deno_graph::source::Loader;
use deno_runtime::deno_permissions::PermissionsContainer;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
@ -112,7 +112,7 @@ pub struct FetchCacher {
global_http_cache: Arc<GlobalHttpCache>, global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>, module_info_cache: Arc<ModuleInfoCache>,
permissions: PermissionsContainer, permissions: FetchPermissionsOption,
cache_info_enabled: bool, cache_info_enabled: bool,
} }
@ -123,7 +123,7 @@ impl FetchCacher {
global_http_cache: Arc<GlobalHttpCache>, global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>, module_info_cache: Arc<ModuleInfoCache>,
permissions: PermissionsContainer, permissions: FetchPermissionsOption,
) -> Self { ) -> Self {
Self { Self {
file_fetcher, file_fetcher,
@ -230,7 +230,7 @@ impl Loader for FetchCacher {
.fetch_no_follow_with_options(FetchNoFollowOptions { .fetch_no_follow_with_options(FetchNoFollowOptions {
fetch_options: FetchOptions { fetch_options: FetchOptions {
specifier: &specifier, specifier: &specifier,
permissions: &permissions, permissions: permissions.as_ref(),
maybe_accept: None, maybe_accept: None,
maybe_cache_setting: maybe_cache_setting.as_ref(), maybe_cache_setting: maybe_cache_setting.as_ref(),
}, },

View file

@ -65,10 +65,13 @@ use deno_core::FeatureChecker;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv; use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_runtime::deno_node::NodeResolver; 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::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore; use deno_runtime::deno_web::BlobStore;
use deno_runtime::inspector_server::InspectorServer; use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use log::warn; use log::warn;
use node_resolver::analyze::NodeCodeTranslator; use node_resolver::analyze::NodeCodeTranslator;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@ -181,6 +184,7 @@ struct CliFactoryServices {
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>, node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
node_resolver: Deferred<Arc<NodeResolver>>, node_resolver: Deferred<Arc<NodeResolver>>,
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>, npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
sloppy_imports_resolver: Deferred<Option<Arc<SloppyImportsResolver>>>, sloppy_imports_resolver: Deferred<Option<Arc<SloppyImportsResolver>>>,
text_only_progress_bar: Deferred<ProgressBar>, text_only_progress_bar: Deferred<ProgressBar>,
type_checker: Deferred<Arc<TypeChecker>>, type_checker: Deferred<Arc<TypeChecker>>,
@ -708,6 +712,15 @@ impl CliFactory {
.await .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> { pub fn feature_checker(&self) -> Result<&Arc<FeatureChecker>, AnyError> {
self.services.feature_checker.get_or_try_init(|| { self.services.feature_checker.get_or_try_init(|| {
let cli_options = self.cli_options()?; 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( pub async fn create_cli_main_worker_factory(
&self, &self,
) -> Result<CliMainWorkerFactory, AnyError> { ) -> Result<CliMainWorkerFactory, AnyError> {
@ -754,11 +778,17 @@ impl CliFactory {
}; };
Ok(CliMainWorkerFactory::new( Ok(CliMainWorkerFactory::new(
StorageKeyResolver::from_options(cli_options),
cli_options.sub_command().clone(),
npm_resolver.clone(),
node_resolver.clone(),
self.blob_store().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( Box::new(CliModuleLoaderFactory::new(
cli_options, cli_options,
if cli_options.code_cache_enabled() { if cli_options.code_cache_enabled() {
@ -779,17 +809,12 @@ impl CliFactory {
self.parsed_source_cache().clone(), self.parsed_source_cache().clone(),
self.resolver().await?.clone(), self.resolver().await?.clone(),
)), )),
node_resolver.clone(),
npm_resolver.clone(),
self.permission_desc_parser()?.clone(),
self.root_cert_store_provider().clone(), self.root_cert_store_provider().clone(),
self.fs().clone(), StorageKeyResolver::from_options(cli_options),
maybe_file_watcher_communicator, cli_options.sub_command().clone(),
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
},
self.create_cli_main_worker_options()?, self.create_cli_main_worker_options()?,
)) ))
} }

View file

@ -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 struct FetchOptions<'a> {
pub specifier: &'a ModuleSpecifier, pub specifier: &'a ModuleSpecifier,
pub permissions: &'a PermissionsContainer, pub permissions: FetchPermissionsOptionRef<'a>,
pub maybe_accept: Option<&'a str>, pub maybe_accept: Option<&'a str>,
pub maybe_cache_setting: Option<&'a CacheSetting>, pub maybe_cache_setting: Option<&'a CacheSetting>,
} }
@ -515,11 +544,33 @@ 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. /// Fetch a source file and asynchronously return it.
#[allow(dead_code)] // todo(25469): undo when merging
#[inline(always)]
pub async fn fetch( pub async fn fetch(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
permissions: &PermissionsContainer, 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> { ) -> Result<File, AnyError> {
self self
.fetch_with_options(FetchOptions { .fetch_with_options(FetchOptions {
@ -583,7 +634,14 @@ impl FileFetcher {
specifier specifier
); );
let scheme = get_validated_scheme(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) { if let Some(file) = self.memory_files.get(specifier) {
Ok(FileOrRedirect::File(file)) Ok(FileOrRedirect::File(file))
} else if scheme == "file" { } else if scheme == "file" {
@ -684,9 +742,7 @@ mod tests {
async fn test_fetch(specifier: &ModuleSpecifier) -> (File, FileFetcher) { async fn test_fetch(specifier: &ModuleSpecifier) -> (File, FileFetcher) {
let (file_fetcher, _) = setup(CacheSetting::ReloadAll, None); let (file_fetcher, _) = setup(CacheSetting::ReloadAll, None);
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(specifier).await;
.fetch(specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
(result.unwrap(), file_fetcher) (result.unwrap(), file_fetcher)
} }
@ -700,7 +756,7 @@ mod tests {
.fetch_with_options_and_max_redirect( .fetch_with_options_and_max_redirect(
FetchOptions { FetchOptions {
specifier, specifier,
permissions: &PermissionsContainer::allow_all(), permissions: FetchPermissionsOptionRef::AllowAll,
maybe_accept: None, maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting), maybe_cache_setting: Some(&file_fetcher.cache_setting),
}, },
@ -796,9 +852,7 @@ mod tests {
}; };
file_fetcher.insert_memory_files(file.clone()); file_fetcher.insert_memory_files(file.clone());
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let result_file = result.unwrap(); let result_file = result.unwrap();
assert_eq!(result_file, file); assert_eq!(result_file, file);
@ -809,9 +863,7 @@ mod tests {
let (file_fetcher, _) = setup(CacheSetting::Use, None); let (file_fetcher, _) = setup(CacheSetting::Use, None);
let specifier = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap(); let specifier = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap(); let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!( assert_eq!(
@ -840,9 +892,7 @@ mod tests {
None, None,
); );
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap(); let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!( assert_eq!(
@ -862,9 +912,7 @@ mod tests {
let specifier = let specifier =
ModuleSpecifier::parse("http://localhost:4545/subdir/mod2.ts").unwrap(); ModuleSpecifier::parse("http://localhost:4545/subdir/mod2.ts").unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap(); let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!( assert_eq!(
@ -882,9 +930,7 @@ mod tests {
.set(&specifier, headers.clone(), file.source.as_bytes()) .set(&specifier, headers.clone(), file.source.as_bytes())
.unwrap(); .unwrap();
let result = file_fetcher_01 let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap(); let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!( assert_eq!(
@ -908,9 +954,7 @@ mod tests {
.set(&specifier, headers.clone(), file.source.as_bytes()) .set(&specifier, headers.clone(), file.source.as_bytes())
.unwrap(); .unwrap();
let result = file_fetcher_02 let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap(); let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!( assert_eq!(
@ -933,9 +977,7 @@ mod tests {
Default::default(), Default::default(),
None, None,
); );
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap(); let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!( assert_eq!(
@ -966,9 +1008,7 @@ mod tests {
None, None,
); );
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let cache_key = let cache_key =
file_fetcher.http_cache.cache_item_key(&specifier).unwrap(); file_fetcher.http_cache.cache_item_key(&specifier).unwrap();
@ -1002,9 +1042,7 @@ mod tests {
Default::default(), Default::default(),
None, None,
); );
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let cache_key = let cache_key =
@ -1041,9 +1079,7 @@ mod tests {
resolve_url("http://localhost:4545/subdir/redirects/redirect1.js") resolve_url("http://localhost:4545/subdir/redirects/redirect1.js")
.unwrap(); .unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap(); let file = result.unwrap();
assert_eq!(file.specifier, redirected_specifier); assert_eq!(file.specifier, redirected_specifier);
@ -1082,9 +1118,7 @@ mod tests {
resolve_url("http://localhost:4545/subdir/redirects/redirect1.js") resolve_url("http://localhost:4545/subdir/redirects/redirect1.js")
.unwrap(); .unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap(); let file = result.unwrap();
assert_eq!(file.specifier, redirected_02_specifier); assert_eq!(file.specifier, redirected_02_specifier);
@ -1142,9 +1176,7 @@ mod tests {
None, None,
); );
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let cache_key = file_fetcher let cache_key = file_fetcher
@ -1182,7 +1214,7 @@ mod tests {
None, None,
); );
let result = file_fetcher let result = file_fetcher
.fetch(&redirected_specifier, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(&redirected_specifier)
.await; .await;
assert!(result.is_ok()); assert!(result.is_ok());
@ -1223,7 +1255,7 @@ mod tests {
.fetch_with_options_and_max_redirect( .fetch_with_options_and_max_redirect(
FetchOptions { FetchOptions {
specifier: &specifier, specifier: &specifier,
permissions: &PermissionsContainer::allow_all(), permissions: FetchPermissionsOptionRef::AllowAll,
maybe_accept: None, maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting), maybe_cache_setting: Some(&file_fetcher.cache_setting),
}, },
@ -1236,7 +1268,7 @@ mod tests {
.fetch_with_options_and_max_redirect( .fetch_with_options_and_max_redirect(
FetchOptions { FetchOptions {
specifier: &specifier, specifier: &specifier,
permissions: &PermissionsContainer::allow_all(), permissions: FetchPermissionsOptionRef::AllowAll,
maybe_accept: None, maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting), maybe_cache_setting: Some(&file_fetcher.cache_setting),
}, },
@ -1264,9 +1296,7 @@ mod tests {
resolve_url("http://localhost:4550/subdir/redirects/redirect1.js") resolve_url("http://localhost:4550/subdir/redirects/redirect1.js")
.unwrap(); .unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap(); let file = result.unwrap();
assert_eq!(file.specifier, redirected_specifier); assert_eq!(file.specifier, redirected_specifier);
@ -1310,9 +1340,7 @@ mod tests {
let specifier = let specifier =
resolve_url("http://localhost:4545/run/002_hello.ts").unwrap(); resolve_url("http://localhost:4545/run/002_hello.ts").unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_err()); assert!(result.is_err());
let err = result.unwrap_err(); let err = result.unwrap_err();
assert_eq!(get_custom_error_class(&err), Some("NoRemote")); assert_eq!(get_custom_error_class(&err), Some("NoRemote"));
@ -1343,22 +1371,16 @@ mod tests {
let specifier = let specifier =
resolve_url("http://localhost:4545/run/002_hello.ts").unwrap(); resolve_url("http://localhost:4545/run/002_hello.ts").unwrap();
let result = file_fetcher_01 let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_err()); assert!(result.is_err());
let err = result.unwrap_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!(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")); assert_eq!(get_custom_error_class(&err), Some("NotCached"));
let result = file_fetcher_02 let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let result = file_fetcher_01 let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
} }
@ -1368,17 +1390,13 @@ mod tests {
let fixture_path = temp_dir.path().join("mod.ts"); let fixture_path = temp_dir.path().join("mod.ts");
let specifier = ModuleSpecifier::from_file_path(&fixture_path).unwrap(); let specifier = ModuleSpecifier::from_file_path(&fixture_path).unwrap();
fs::write(fixture_path.clone(), r#"console.log("hello deno");"#).unwrap(); fs::write(fixture_path.clone(), r#"console.log("hello deno");"#).unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap(); let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(&*file.source, r#"console.log("hello deno");"#); assert_eq!(&*file.source, r#"console.log("hello deno");"#);
fs::write(fixture_path, r#"console.log("goodbye deno");"#).unwrap(); fs::write(fixture_path, r#"console.log("goodbye deno");"#).unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap().into_text_decoded().unwrap(); let file = result.unwrap().into_text_decoded().unwrap();
assert_eq!(&*file.source, r#"console.log("goodbye deno");"#); assert_eq!(&*file.source, r#"console.log("goodbye deno");"#);
@ -1392,18 +1410,14 @@ mod tests {
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone())); setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
let specifier = let specifier =
ModuleSpecifier::parse("http://localhost:4545/dynamic").unwrap(); ModuleSpecifier::parse("http://localhost:4545/dynamic").unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap(); let file = result.unwrap();
let first = file.source; let first = file.source;
let (file_fetcher, _) = let (file_fetcher, _) =
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone())); setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap(); let file = result.unwrap();
let second = file.source; let second = file.source;
@ -1419,18 +1433,14 @@ mod tests {
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone())); setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
let specifier = let specifier =
ModuleSpecifier::parse("http://localhost:4545/dynamic_cache").unwrap(); ModuleSpecifier::parse("http://localhost:4545/dynamic_cache").unwrap();
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap(); let file = result.unwrap();
let first = file.source; let first = file.source;
let (file_fetcher, _) = let (file_fetcher, _) =
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone())); setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
let result = file_fetcher let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await;
assert!(result.is_ok()); assert!(result.is_ok());
let file = result.unwrap(); let file = result.unwrap();
let second = file.source; let second = file.source;

View file

@ -9,9 +9,9 @@ use deno_core::error::AnyError;
use deno_core::parking_lot::RwLock; use deno_core::parking_lot::RwLock;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use deno_runtime::colors; use deno_runtime::colors;
use deno_runtime::deno_permissions::PermissionsContainer;
use crate::args::CliOptions; use crate::args::CliOptions;
use crate::file_fetcher::FetchPermissionsOption;
use crate::module_loader::ModuleLoadPreparer; use crate::module_loader::ModuleLoadPreparer;
use crate::util::fs::collect_specifiers; use crate::util::fs::collect_specifiers;
use crate::util::path::is_script_ext; use crate::util::path::is_script_ext;
@ -75,7 +75,7 @@ impl MainModuleGraphContainer {
specifiers, specifiers,
false, false,
self.cli_options.ts_type_lib_window(), self.cli_options.ts_type_lib_window(),
PermissionsContainer::allow_all(), FetchPermissionsOption::AllowAll,
) )
.await?; .await?;
graph_permit.commit(); graph_permit.commit();

View file

@ -11,6 +11,7 @@ use crate::cache::ModuleInfoCache;
use crate::cache::ParsedSourceCache; use crate::cache::ParsedSourceCache;
use crate::colors; use crate::colors;
use crate::errors::get_error_class_name; use crate::errors::get_error_class_name;
use crate::file_fetcher::FetchPermissionsOption;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::resolver::CliGraphResolver; use crate::resolver::CliGraphResolver;
@ -41,7 +42,6 @@ use deno_graph::ResolutionError;
use deno_graph::SpecifierError; use deno_graph::SpecifierError;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node; use deno_runtime::deno_node;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::jsr::JsrDepPackageReq; use deno_semver::jsr::JsrDepPackageReq;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::Version; use deno_semver::Version;
@ -670,12 +670,12 @@ impl ModuleGraphBuilder {
/// Creates the default loader used for creating a graph. /// Creates the default loader used for creating a graph.
pub fn create_graph_loader(&self) -> cache::FetchCacher { 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( pub fn create_fetch_cacher(
&self, &self,
permissions: PermissionsContainer, permissions: FetchPermissionsOption,
) -> cache::FetchCacher { ) -> cache::FetchCacher {
cache::FetchCacher::new( cache::FetchCacher::new(
self.file_fetcher.clone(), self.file_fetcher.clone(),

View file

@ -6,7 +6,6 @@ use dashmap::DashMap;
use deno_core::serde_json; use deno_core::serde_json;
use deno_graph::packages::JsrPackageInfo; use deno_graph::packages::JsrPackageInfo;
use deno_graph::packages::JsrPackageVersionInfo; use deno_graph::packages::JsrPackageVersionInfo;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use std::sync::Arc; use std::sync::Arc;
@ -68,10 +67,7 @@ impl JsrFetchResolver {
let file_fetcher = self.file_fetcher.clone(); let file_fetcher = self.file_fetcher.clone();
// spawn due to the lsp's `Send` requirement // spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move { let file = deno_core::unsync::spawn(async move {
file_fetcher file_fetcher.fetch_bypass_permissions(&meta_url).await.ok()
.fetch(&meta_url, &PermissionsContainer::allow_all())
.await
.ok()
}) })
.await .await
.ok()??; .ok()??;
@ -96,10 +92,7 @@ impl JsrFetchResolver {
let file_fetcher = self.file_fetcher.clone(); let file_fetcher = self.file_fetcher.clone();
// spawn due to the lsp's `Send` requirement // spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move { let file = deno_core::unsync::spawn(async move {
file_fetcher file_fetcher.fetch_bypass_permissions(&meta_url).await.ok()
.fetch(&meta_url, &PermissionsContainer::allow_all())
.await
.ok()
}) })
.await .await
.ok()??; .ok()??;

View file

@ -37,7 +37,6 @@ use deno_lint::linter::LintConfig as DenoLintConfig;
use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonCache; use deno_package_json::PackageJsonCache;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::fs_util::specifier_to_file_path; use deno_runtime::fs_util::specifier_to_file_path;
use indexmap::IndexSet; use indexmap::IndexSet;
use lsp_types::ClientCapabilities; use lsp_types::ClientCapabilities;
@ -1509,13 +1508,12 @@ impl ConfigData {
ConfigWatchedFileType::ImportMap, ConfigWatchedFileType::ImportMap,
); );
// spawn due to the lsp's `Send` requirement // spawn due to the lsp's `Send` requirement
let fetch_result = deno_core::unsync::spawn({ let fetch_result =
deno_core::unsync::spawn({
let file_fetcher = file_fetcher.cloned().unwrap(); let file_fetcher = file_fetcher.cloned().unwrap();
let import_map_url = import_map_url.clone(); let import_map_url = import_map_url.clone();
async move { async move {
file_fetcher file_fetcher.fetch_bypass_permissions(&import_map_url).await
.fetch(&import_map_url, &PermissionsContainer::allow_all())
.await
} }
}) })
.await .await
@ -1558,7 +1556,7 @@ impl ConfigData {
let file_fetcher = file_fetcher.clone().unwrap(); let file_fetcher = file_fetcher.clone().unwrap();
async move { async move {
let file = file_fetcher let file = file_fetcher
.fetch(&specifier, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(&specifier)
.await? .await?
.into_text_decoded()?; .into_text_decoded()?;
Ok(file.source.to_string()) Ok(file.source.to_string())

View file

@ -14,7 +14,6 @@ use deno_graph::packages::JsrPackageInfo;
use deno_graph::packages::JsrPackageInfoVersion; use deno_graph::packages::JsrPackageInfoVersion;
use deno_graph::packages::JsrPackageVersionInfo; use deno_graph::packages::JsrPackageVersionInfo;
use deno_graph::ModuleSpecifier; use deno_graph::ModuleSpecifier;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::jsr::JsrPackageReqReference; use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
@ -311,7 +310,7 @@ impl PackageSearchApi for CliJsrSearchApi {
// spawn due to the lsp's `Send` requirement // spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move { let file = deno_core::unsync::spawn(async move {
file_fetcher file_fetcher
.fetch(&search_url, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(&search_url)
.await? .await?
.into_text_decoded() .into_text_decoded()
}) })

View file

@ -4,7 +4,6 @@ use dashmap::DashMap;
use deno_core::anyhow::anyhow; use deno_core::anyhow::anyhow;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::Version; use deno_semver::Version;
use serde::Deserialize; use serde::Deserialize;
@ -55,7 +54,7 @@ impl PackageSearchApi for CliNpmSearchApi {
let file_fetcher = self.file_fetcher.clone(); let file_fetcher = self.file_fetcher.clone();
let file = deno_core::unsync::spawn(async move { let file = deno_core::unsync::spawn(async move {
file_fetcher file_fetcher
.fetch(&search_url, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(&search_url)
.await? .await?
.into_text_decoded() .into_text_decoded()
}) })

View file

@ -16,6 +16,7 @@ use crate::args::CacheSetting;
use crate::cache::GlobalHttpCache; use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache; use crate::cache::HttpCache;
use crate::file_fetcher::FetchOptions; use crate::file_fetcher::FetchOptions;
use crate::file_fetcher::FetchPermissionsOptionRef;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
@ -30,7 +31,6 @@ use deno_core::url::Position;
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::Dependency; use deno_graph::Dependency;
use deno_runtime::deno_permissions::PermissionsContainer;
use log::error; use log::error;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::borrow::Cow; use std::borrow::Cow;
@ -481,7 +481,7 @@ impl ModuleRegistry {
file_fetcher file_fetcher
.fetch_with_options(FetchOptions { .fetch_with_options(FetchOptions {
specifier: &specifier, 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_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, maybe_cache_setting: None,
}) })
@ -584,7 +584,7 @@ impl ModuleRegistry {
let file = deno_core::unsync::spawn({ let file = deno_core::unsync::spawn({
async move { async move {
file_fetcher file_fetcher
.fetch(&endpoint, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(&endpoint)
.await .await
.ok()? .ok()?
.into_text_decoded() .into_text_decoded()
@ -983,7 +983,7 @@ impl ModuleRegistry {
// spawn due to the lsp's `Send` requirement // spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move { let file = deno_core::unsync::spawn(async move {
file_fetcher file_fetcher
.fetch(&specifier, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(&specifier)
.await .await
.ok()? .ok()?
.into_text_decoded() .into_text_decoded()
@ -1049,7 +1049,7 @@ impl ModuleRegistry {
let specifier = specifier.clone(); let specifier = specifier.clone();
async move { async move {
file_fetcher file_fetcher
.fetch(&specifier, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(&specifier)
.await .await
.map_err(|err| { .map_err(|err| {
error!( error!(
@ -1095,7 +1095,7 @@ impl ModuleRegistry {
let specifier = specifier.clone(); let specifier = specifier.clone();
async move { async move {
file_fetcher file_fetcher
.fetch(&specifier, &PermissionsContainer::allow_all()) .fetch_bypass_permissions(&specifier)
.await .await
.map_err(|err| { .map_err(|err| {
error!( error!(

View file

@ -31,6 +31,7 @@ use deno_core::unsync::spawn;
use deno_core::unsync::spawn_blocking; use deno_core::unsync::spawn_blocking;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::tokio_util::create_and_run_current_thread; use deno_runtime::tokio_util::create_and_run_current_thread;
use indexmap::IndexMap; use indexmap::IndexMap;
use std::borrow::Cow; use std::borrow::Cow;
@ -227,8 +228,11 @@ impl TestRun {
// Various test files should not share the same permissions in terms of // Various test files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one // `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable. // file would have impact on other files, which is undesirable.
let permissions = let permission_desc_parser = factory.permission_desc_parser()?.clone();
Permissions::from_options(&cli_options.permissions_options()?)?; let permissions = Permissions::from_options(
permission_desc_parser.as_ref(),
&cli_options.permissions_options(),
)?;
let main_graph_container = factory.main_module_graph_container().await?; let main_graph_container = factory.main_module_graph_container().await?;
test::check_specifiers( test::check_specifiers(
factory.file_fetcher()?, factory.file_fetcher()?,
@ -276,7 +280,10 @@ impl TestRun {
let join_handles = queue.into_iter().map(move |specifier| { let join_handles = queue.into_iter().map(move |specifier| {
let specifier = specifier.clone(); let specifier = specifier.clone();
let worker_factory = worker_factory.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 worker_sender = test_event_sender_factory.worker();
let fail_fast_tracker = fail_fast_tracker.clone(); let fail_fast_tracker = fail_fast_tracker.clone();
let lsp_filter = self.filters.get(&specifier); let lsp_filter = self.filters.get(&specifier);
@ -305,7 +312,7 @@ impl TestRun {
// channel. // channel.
create_and_run_current_thread(test::test_specifier( create_and_run_current_thread(test::test_specifier(
worker_factory, worker_factory,
permissions, permissions_container,
specifier, specifier,
worker_sender, worker_sender,
fail_fast_tracker, fail_fast_tracker,

View file

@ -104,7 +104,7 @@ impl ModuleLoadPreparer {
roots: &[ModuleSpecifier], roots: &[ModuleSpecifier],
is_dynamic: bool, is_dynamic: bool,
lib: TsTypeLib, lib: TsTypeLib,
permissions: PermissionsContainer, permissions: crate::file_fetcher::FetchPermissionsOption,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
log::debug!("Preparing module load."); log::debug!("Preparing module load.");
let _pb_clear_guard = self.progress_bar.clear_guard(); let _pb_clear_guard = self.progress_bar.clear_guard();
@ -762,7 +762,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
&[specifier], &[specifier],
is_dynamic, is_dynamic,
lib, lib,
root_permissions, root_permissions.into(),
) )
.await?; .await?;
update_permit.commit(); update_permit.commit();

View file

@ -280,7 +280,7 @@ impl NodeRequireResolver for ByonmCliNpmResolver {
.components() .components()
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules") .any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
{ {
permissions.check_read(path)?; _ = permissions.check_read_path(path)?;
} }
Ok(()) Ok(())
} }

View file

@ -127,7 +127,8 @@ impl RegistryReadPermissionChecker {
} }
} }
permissions.check_read(path) _ = permissions.check_read_path(path)?;
Ok(())
} }
} }

View file

@ -15,7 +15,6 @@ use deno_core::serde_json;
use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmPackageInfo;
use deno_runtime::deno_node::NodeRequireResolver; use deno_runtime::deno_node::NodeRequireResolver;
use deno_runtime::deno_node::NpmProcessStateProvider; use deno_runtime::deno_node::NpmProcessStateProvider;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use node_resolver::NpmResolver; use node_resolver::NpmResolver;
@ -152,10 +151,7 @@ impl NpmFetchResolver {
let file_fetcher = self.file_fetcher.clone(); let file_fetcher = self.file_fetcher.clone();
// spawn due to the lsp's `Send` requirement // spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move { let file = deno_core::unsync::spawn(async move {
file_fetcher file_fetcher.fetch_bypass_permissions(&info_url).await.ok()
.fetch(&info_url, &PermissionsContainer::allow_all())
.await
.ok()
}) })
.await .await
.ok()??; .ok()??;

View file

@ -2,6 +2,7 @@
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time; use std::time;
use deno_core::error::generic_error; use deno_core::error::generic_error;
@ -13,6 +14,7 @@ use deno_core::ModuleSpecifier;
use deno_core::OpState; use deno_core::OpState;
use deno_runtime::deno_permissions::create_child_permissions; use deno_runtime::deno_permissions::create_child_permissions;
use deno_runtime::deno_permissions::ChildPermissionsArg; use deno_runtime::deno_permissions::ChildPermissionsArg;
use deno_runtime::deno_permissions::PermissionDescriptorParser;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use uuid::Uuid; use uuid::Uuid;
@ -59,11 +61,18 @@ pub fn op_pledge_test_permissions(
#[serde] args: ChildPermissionsArg, #[serde] args: ChildPermissionsArg,
) -> Result<Uuid, AnyError> { ) -> Result<Uuid, AnyError> {
let token = Uuid::new_v4(); let token = Uuid::new_v4();
let permission_desc_parser = state
.borrow::<Arc<dyn PermissionDescriptorParser>>()
.clone();
let parent_permissions = state.borrow_mut::<PermissionsContainer>(); let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = { let worker_permissions = {
let mut parent_permissions = parent_permissions.0.lock(); let mut parent_permissions = parent_permissions.inner.lock();
let perms = create_child_permissions(&mut parent_permissions, args)?; let perms = create_child_permissions(
PermissionsContainer::new(perms) permission_desc_parser.as_ref(),
&mut parent_permissions,
args,
)?;
PermissionsContainer::new(permission_desc_parser, perms)
}; };
let parent_permissions = parent_permissions.clone(); let parent_permissions = parent_permissions.clone();
@ -74,7 +83,7 @@ pub fn op_pledge_test_permissions(
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions)); state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
// NOTE: This call overrides current permission set for the worker // 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); state.put::<PermissionsContainer>(worker_permissions);
Ok(token) Ok(token)
@ -91,7 +100,7 @@ pub fn op_restore_test_permissions(
} }
let permissions = permissions_holder.1; let permissions = permissions_holder.1;
state.put(permissions.0.clone()); state.put(permissions.inner.clone());
state.put::<PermissionsContainer>(permissions); state.put::<PermissionsContainer>(permissions);
Ok(()) Ok(())
} else { } else {

View file

@ -18,9 +18,11 @@ use deno_core::ModuleSpecifier;
use deno_core::OpState; use deno_core::OpState;
use deno_runtime::deno_permissions::create_child_permissions; use deno_runtime::deno_permissions::create_child_permissions;
use deno_runtime::deno_permissions::ChildPermissionsArg; use deno_runtime::deno_permissions::ChildPermissionsArg;
use deno_runtime::deno_permissions::PermissionDescriptorParser;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::Arc;
use uuid::Uuid; use uuid::Uuid;
deno_core::extension!(deno_test, deno_core::extension!(deno_test,
@ -54,11 +56,18 @@ pub fn op_pledge_test_permissions(
#[serde] args: ChildPermissionsArg, #[serde] args: ChildPermissionsArg,
) -> Result<Uuid, AnyError> { ) -> Result<Uuid, AnyError> {
let token = Uuid::new_v4(); let token = Uuid::new_v4();
let permission_desc_parser = state
.borrow::<Arc<dyn PermissionDescriptorParser>>()
.clone();
let parent_permissions = state.borrow_mut::<PermissionsContainer>(); let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = { let worker_permissions = {
let mut parent_permissions = parent_permissions.0.lock(); let mut parent_permissions = parent_permissions.inner.lock();
let perms = create_child_permissions(&mut parent_permissions, args)?; let perms = create_child_permissions(
PermissionsContainer::new(perms) permission_desc_parser.as_ref(),
&mut parent_permissions,
args,
)?;
PermissionsContainer::new(permission_desc_parser, perms)
}; };
let parent_permissions = parent_permissions.clone(); let parent_permissions = parent_permissions.clone();
@ -68,7 +77,7 @@ pub fn op_pledge_test_permissions(
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions)); state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
// NOTE: This call overrides current permission set for the worker // 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); state.put::<PermissionsContainer>(worker_permissions);
Ok(token) Ok(token)
@ -85,7 +94,7 @@ pub fn op_restore_test_permissions(
} }
let permissions = permissions_holder.1; let permissions = permissions_holder.1;
state.put(permissions.0.clone()); state.put(permissions.inner.clone());
state.put::<PermissionsContainer>(permissions); state.put::<PermissionsContainer>(permissions);
Ok(()) Ok(())
} else { } else {

View file

@ -32,6 +32,8 @@ use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore;
use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::WorkerExecutionMode; use deno_runtime::WorkerExecutionMode;
use deno_runtime::WorkerLogLevel; use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference; 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_path = std::env::current_exe().unwrap();
let current_exe_name = let current_exe_name =
current_exe_path.file_name().unwrap().to_string_lossy(); 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 deno_dir_provider = Arc::new(DenoDirProvider::new(None));
let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider { let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider {
ca_stores: metadata.ca_stores, ca_stores: metadata.ca_stores,
@ -660,8 +661,7 @@ pub async fn run(
}; };
let permissions = { let permissions = {
let mut permissions = let mut permissions = metadata.permissions.to_options();
metadata.permissions.to_options(maybe_cwd.as_deref())?;
// if running with an npm vfs, grant read access to it // if running with an npm vfs, grant read access to it
if let Some(vfs_root) = maybe_vfs_root { if let Some(vfs_root) = maybe_vfs_root {
match &mut permissions.allow_read { match &mut permissions.allow_read {
@ -669,15 +669,20 @@ pub async fn run(
// do nothing, already granted // do nothing, already granted
} }
Some(vec) => { Some(vec) => {
vec.push(vfs_root); vec.push(vfs_root.to_string_lossy().to_string());
} }
None => { 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 feature_checker = Arc::new({
let mut checker = FeatureChecker::default(); let mut checker = FeatureChecker::default();
@ -689,21 +694,24 @@ pub async fn run(
} }
checker checker
}); });
let permission_desc_parser =
Arc::new(RuntimePermissionDescriptorParser::new(fs.clone()));
let worker_factory = CliMainWorkerFactory::new( let worker_factory = CliMainWorkerFactory::new(
StorageKeyResolver::empty(), Arc::new(BlobStore::default()),
crate::args::DenoSubcommand::Run(Default::default()), // Code cache is not supported for standalone binary yet.
npm_resolver, None,
node_resolver, feature_checker,
Default::default(),
Box::new(module_loader_factory),
root_cert_store_provider,
fs, fs,
None, None,
None, None,
None, None,
feature_checker, Box::new(module_loader_factory),
// Code cache is not supported for standalone binary yet. node_resolver,
None, npm_resolver,
permission_desc_parser,
root_cert_store_provider,
StorageKeyResolver::empty(),
crate::args::DenoSubcommand::Run(Default::default()),
CliMainWorkerOptions { CliMainWorkerOptions {
argv: metadata.argv, argv: metadata.argv,
log_level: WorkerLogLevel::Info, log_level: WorkerLogLevel::Info,

View file

@ -30,6 +30,7 @@ use deno_core::ModuleSpecifier;
use deno_core::PollEventLoopOptions; use deno_core::PollEventLoopOptions;
use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::tokio_util::create_and_run_current_thread; use deno_runtime::tokio_util::create_and_run_current_thread;
use deno_runtime::WorkerExecutionMode; use deno_runtime::WorkerExecutionMode;
use indexmap::IndexMap; use indexmap::IndexMap;
@ -144,14 +145,14 @@ fn create_reporter(
/// Run a single specifier as an executable bench module. /// Run a single specifier as an executable bench module.
async fn bench_specifier( async fn bench_specifier(
worker_factory: Arc<CliMainWorkerFactory>, worker_factory: Arc<CliMainWorkerFactory>,
permissions: Permissions, permissions_container: PermissionsContainer,
specifier: ModuleSpecifier, specifier: ModuleSpecifier,
sender: UnboundedSender<BenchEvent>, sender: UnboundedSender<BenchEvent>,
filter: TestFilter, filter: TestFilter,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
match bench_specifier_inner( match bench_specifier_inner(
worker_factory, worker_factory,
permissions, permissions_container,
specifier.clone(), specifier.clone(),
&sender, &sender,
filter, filter,
@ -176,7 +177,7 @@ async fn bench_specifier(
/// Run a single specifier as an executable bench module. /// Run a single specifier as an executable bench module.
async fn bench_specifier_inner( async fn bench_specifier_inner(
worker_factory: Arc<CliMainWorkerFactory>, worker_factory: Arc<CliMainWorkerFactory>,
permissions: Permissions, permissions_container: PermissionsContainer,
specifier: ModuleSpecifier, specifier: ModuleSpecifier,
sender: &UnboundedSender<BenchEvent>, sender: &UnboundedSender<BenchEvent>,
filter: TestFilter, filter: TestFilter,
@ -185,7 +186,7 @@ async fn bench_specifier_inner(
.create_custom_worker( .create_custom_worker(
WorkerExecutionMode::Bench, WorkerExecutionMode::Bench,
specifier.clone(), specifier.clone(),
PermissionsContainer::new(permissions), permissions_container,
vec![ops::bench::deno_bench::init_ops(sender.clone())], vec![ops::bench::deno_bench::init_ops(sender.clone())],
Default::default(), Default::default(),
) )
@ -264,6 +265,7 @@ async fn bench_specifier_inner(
async fn bench_specifiers( async fn bench_specifiers(
worker_factory: Arc<CliMainWorkerFactory>, worker_factory: Arc<CliMainWorkerFactory>,
permissions: &Permissions, permissions: &Permissions,
permissions_desc_parser: &Arc<RuntimePermissionDescriptorParser>,
specifiers: Vec<ModuleSpecifier>, specifiers: Vec<ModuleSpecifier>,
options: BenchSpecifierOptions, options: BenchSpecifierOptions,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
@ -273,13 +275,16 @@ async fn bench_specifiers(
let join_handles = specifiers.into_iter().map(move |specifier| { let join_handles = specifiers.into_iter().map(move |specifier| {
let worker_factory = worker_factory.clone(); 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 sender = sender.clone();
let options = option_for_handles.clone(); let options = option_for_handles.clone();
spawn_blocking(move || { spawn_blocking(move || {
let future = bench_specifier( let future = bench_specifier(
worker_factory, worker_factory,
permissions, permissions_container,
specifier, specifier,
sender, sender,
options.filter, options.filter,
@ -410,8 +415,11 @@ pub async fn run_benchmarks(
// Various bench files should not share the same permissions in terms of // Various bench files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one // `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable. // file would have impact on other files, which is undesirable.
let permissions = let permission_desc_parser = factory.permission_desc_parser()?.clone();
Permissions::from_options(&cli_options.permissions_options()?)?; let permissions = Permissions::from_options(
permission_desc_parser.as_ref(),
&cli_options.permissions_options(),
)?;
let members_with_bench_options = let members_with_bench_options =
cli_options.resolve_bench_options_for_members(&bench_flags)?; cli_options.resolve_bench_options_for_members(&bench_flags)?;
@ -446,6 +454,7 @@ pub async fn run_benchmarks(
bench_specifiers( bench_specifiers(
worker_factory, worker_factory,
&permissions, &permissions,
&permission_desc_parser,
specifiers, specifiers,
BenchSpecifierOptions { BenchSpecifierOptions {
filter: TestFilter::from_flag(&workspace_bench_options.filter), 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 // Various bench files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one // `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable. // file would have impact on other files, which is undesirable.
let permissions = let permission_desc_parser = factory.permission_desc_parser()?.clone();
Permissions::from_options(&cli_options.permissions_options()?)?; let permissions = Permissions::from_options(
permission_desc_parser.as_ref(),
&cli_options.permissions_options(),
)?;
let graph = module_graph_creator let graph = module_graph_creator
.create_graph(graph_kind, collected_bench_modules.clone()) .create_graph(graph_kind, collected_bench_modules.clone())
@ -568,6 +580,7 @@ pub async fn run_benchmarks_with_watch(
bench_specifiers( bench_specifiers(
worker_factory, worker_factory,
&permissions, &permissions,
&permission_desc_parser,
specifiers, specifiers,
BenchSpecifierOptions { BenchSpecifierOptions {
filter: TestFilter::from_flag(&workspace_bench_options.filter), filter: TestFilter::from_flag(&workspace_bench_options.filter),

View file

@ -25,7 +25,6 @@ use deno_core::serde_json::json;
use deno_core::url::Url; use deno_core::url::Url;
use deno_runtime::deno_io::Stdio; use deno_runtime::deno_io::Stdio;
use deno_runtime::deno_io::StdioPipe; use deno_runtime::deno_io::StdioPipe;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::WorkerExecutionMode; use deno_runtime::WorkerExecutionMode;
use deno_terminal::colors; use deno_terminal::colors;
@ -65,7 +64,8 @@ pub async fn kernel(
resolve_url_or_path("./$deno$jupyter.ts", cli_options.initial_cwd()) resolve_url_or_path("./$deno$jupyter.ts", cli_options.initial_cwd())
.unwrap(); .unwrap();
// TODO(bartlomieju): should we run with all permissions? // 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 npm_resolver = factory.npm_resolver().await?.clone();
let resolver = factory.resolver().await?.clone(); let resolver = factory.resolver().await?.clone();
let worker_factory = factory.create_cli_main_worker_factory().await?; let worker_factory = factory.create_cli_main_worker_factory().await?;

View file

@ -106,7 +106,7 @@ pub async fn cache_top_level_deps(
&roots, &roots,
false, false,
deno_config::deno_json::TsTypeLib::DenoWorker, deno_config::deno_json::TsTypeLib::DenoWorker,
deno_runtime::deno_permissions::PermissionsContainer::allow_all(), crate::file_fetcher::FetchPermissionsOption::AllowAll,
) )
.await?; .await?;
} }

View file

@ -16,8 +16,6 @@ use deno_core::error::AnyError;
use deno_core::futures::StreamExt; use deno_core::futures::StreamExt;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::unsync::spawn_blocking; use deno_core::unsync::spawn_blocking;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::WorkerExecutionMode; use deno_runtime::WorkerExecutionMode;
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
@ -151,9 +149,7 @@ async fn read_eval_file(
let specifier = let specifier =
deno_core::resolve_url_or_path(eval_file, cli_options.initial_cwd())?; deno_core::resolve_url_or_path(eval_file, cli_options.initial_cwd())?;
let file = file_fetcher let file = file_fetcher.fetch_bypass_permissions(&specifier).await?;
.fetch(&specifier, &PermissionsContainer::allow_all())
.await?;
Ok(file.into_text_decoded()?.source) Ok(file.into_text_decoded()?.source)
} }
@ -166,9 +162,7 @@ pub async fn run(
let factory = CliFactory::from_flags(flags); let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?; let cli_options = factory.cli_options()?;
let main_module = cli_options.resolve_main_module()?; let main_module = cli_options.resolve_main_module()?;
let permissions = PermissionsContainer::new(Permissions::from_options( let permissions = factory.create_permissions_container()?;
&cli_options.permissions_options()?,
)?);
let npm_resolver = factory.npm_resolver().await?.clone(); let npm_resolver = factory.npm_resolver().await?.clone();
let resolver = factory.resolver().await?.clone(); let resolver = factory.resolver().await?.clone();
let file_fetcher = factory.file_fetcher()?; let file_fetcher = factory.file_fetcher()?;

View file

@ -5,8 +5,6 @@ use std::sync::Arc;
use deno_config::deno_json::NodeModulesDirMode; use deno_config::deno_json::NodeModulesDirMode;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::WorkerExecutionMode; use deno_runtime::WorkerExecutionMode;
use crate::args::EvalFlags; use crate::args::EvalFlags;
@ -62,9 +60,7 @@ pub async fn run_script(
maybe_npm_install(&factory).await?; maybe_npm_install(&factory).await?;
let permissions = PermissionsContainer::new(Permissions::from_options( let permissions = factory.create_permissions_container()?;
&cli_options.permissions_options()?,
)?);
let worker_factory = factory.create_cli_main_worker_factory().await?; let worker_factory = factory.create_cli_main_worker_factory().await?;
let mut worker = worker_factory let mut worker = worker_factory
.create_main_worker(mode, main_module, permissions) .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 file_fetcher = factory.file_fetcher()?;
let worker_factory = factory.create_cli_main_worker_factory().await?; let worker_factory = factory.create_cli_main_worker_factory().await?;
let permissions = PermissionsContainer::new(Permissions::from_options( let permissions = factory.create_permissions_container()?;
&cli_options.permissions_options()?,
)?);
let mut source = Vec::new(); let mut source = Vec::new();
std::io::stdin().read_to_end(&mut source)?; std::io::stdin().read_to_end(&mut source)?;
// Save a fake file into file fetcher cache // 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 _ = watcher_communicator.watch_paths(cli_options.watch_paths());
let permissions = PermissionsContainer::new(Permissions::from_options( let permissions = factory.create_permissions_container()?;
&cli_options.permissions_options()?,
)?);
let mut worker = factory let mut worker = factory
.create_cli_main_worker_factory() .create_cli_main_worker_factory()
.await? .await?
@ -181,9 +173,7 @@ pub async fn eval_command(
source: source_code.into_bytes().into(), source: source_code.into_bytes().into(),
}); });
let permissions = PermissionsContainer::new(Permissions::from_options( let permissions = factory.create_permissions_container()?;
&cli_options.permissions_options()?,
)?);
let worker_factory = factory.create_cli_main_worker_factory().await?; let worker_factory = factory.create_cli_main_worker_factory().await?;
let mut worker = worker_factory let mut worker = worker_factory
.create_main_worker(WorkerExecutionMode::Eval, main_module, permissions) .create_main_worker(WorkerExecutionMode::Eval, main_module, permissions)

View file

@ -5,7 +5,6 @@ use std::sync::Arc;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::TryFutureExt; use deno_core::futures::TryFutureExt;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use super::run::check_permission_before_script; use super::run::check_permission_before_script;
@ -45,9 +44,7 @@ pub async fn serve(
maybe_npm_install(&factory).await?; maybe_npm_install(&factory).await?;
let permissions = PermissionsContainer::new(Permissions::from_options( let permissions = factory.create_permissions_container()?;
&cli_options.permissions_options()?,
)?);
let worker_factory = factory.create_cli_main_worker_factory().await?; let worker_factory = factory.create_cli_main_worker_factory().await?;
do_serve( do_serve(
@ -175,9 +172,7 @@ async fn serve_with_watch(
let _ = watcher_communicator.watch_paths(cli_options.watch_paths()); let _ = watcher_communicator.watch_paths(cli_options.watch_paths());
let permissions = PermissionsContainer::new(Permissions::from_options( let permissions = factory.create_permissions_container()?;
&cli_options.permissions_options()?,
)?);
let worker_factory = factory.create_cli_main_worker_factory().await?; let worker_factory = factory.create_cli_main_worker_factory().await?;
do_serve(worker_factory, main_module, permissions, worker_count, hmr) do_serve(worker_factory, main_module, permissions, worker_count, hmr)

View file

@ -56,6 +56,7 @@ use deno_runtime::deno_io::StdioPipe;
use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::fmt_errors::format_js_error; 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::tokio_util::create_and_run_current_thread;
use deno_runtime::worker::MainWorker; use deno_runtime::worker::MainWorker;
use deno_runtime::WorkerExecutionMode; use deno_runtime::WorkerExecutionMode;
@ -595,7 +596,7 @@ fn get_test_reporter(options: &TestSpecifiersOptions) -> Box<dyn TestReporter> {
async fn configure_main_worker( async fn configure_main_worker(
worker_factory: Arc<CliMainWorkerFactory>, worker_factory: Arc<CliMainWorkerFactory>,
specifier: &Url, specifier: &Url,
permissions: Permissions, permissions_container: PermissionsContainer,
worker_sender: TestEventWorkerSender, worker_sender: TestEventWorkerSender,
options: &TestSpecifierOptions, options: &TestSpecifierOptions,
) -> Result<(Option<Box<dyn CoverageCollector>>, MainWorker), anyhow::Error> { ) -> Result<(Option<Box<dyn CoverageCollector>>, MainWorker), anyhow::Error> {
@ -603,7 +604,7 @@ async fn configure_main_worker(
.create_custom_worker( .create_custom_worker(
WorkerExecutionMode::Test, WorkerExecutionMode::Test,
specifier.clone(), specifier.clone(),
PermissionsContainer::new(permissions), permissions_container,
vec![ops::testing::deno_test::init_ops(worker_sender.sender)], vec![ops::testing::deno_test::init_ops(worker_sender.sender)],
Stdio { Stdio {
stdin: StdioPipe::inherit(), stdin: StdioPipe::inherit(),
@ -646,7 +647,7 @@ async fn configure_main_worker(
/// both. /// both.
pub async fn test_specifier( pub async fn test_specifier(
worker_factory: Arc<CliMainWorkerFactory>, worker_factory: Arc<CliMainWorkerFactory>,
permissions: Permissions, permissions_container: PermissionsContainer,
specifier: ModuleSpecifier, specifier: ModuleSpecifier,
worker_sender: TestEventWorkerSender, worker_sender: TestEventWorkerSender,
fail_fast_tracker: FailFastTracker, fail_fast_tracker: FailFastTracker,
@ -658,7 +659,7 @@ pub async fn test_specifier(
let (coverage_collector, mut worker) = configure_main_worker( let (coverage_collector, mut worker) = configure_main_worker(
worker_factory, worker_factory,
&specifier, &specifier,
permissions, permissions_container,
worker_sender, worker_sender,
&options, &options,
) )
@ -1327,9 +1328,8 @@ async fn fetch_inline_files(
) -> Result<Vec<File>, AnyError> { ) -> Result<Vec<File>, AnyError> {
let mut files = Vec::new(); let mut files = Vec::new();
for specifier in specifiers { for specifier in specifiers {
let fetch_permissions = PermissionsContainer::allow_all();
let file = file_fetcher let file = file_fetcher
.fetch(&specifier, &fetch_permissions) .fetch_bypass_permissions(&specifier)
.await? .await?
.into_text_decoded()?; .into_text_decoded()?;
@ -1407,6 +1407,7 @@ static HAS_TEST_RUN_SIGINT_HANDLER: AtomicBool = AtomicBool::new(false);
async fn test_specifiers( async fn test_specifiers(
worker_factory: Arc<CliMainWorkerFactory>, worker_factory: Arc<CliMainWorkerFactory>,
permissions: &Permissions, permissions: &Permissions,
permission_desc_parser: &Arc<RuntimePermissionDescriptorParser>,
specifiers: Vec<ModuleSpecifier>, specifiers: Vec<ModuleSpecifier>,
options: TestSpecifiersOptions, options: TestSpecifiersOptions,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
@ -1434,14 +1435,17 @@ async fn test_specifiers(
let join_handles = specifiers.into_iter().map(move |specifier| { let join_handles = specifiers.into_iter().map(move |specifier| {
let worker_factory = worker_factory.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 worker_sender = test_event_sender_factory.worker();
let fail_fast_tracker = fail_fast_tracker.clone(); let fail_fast_tracker = fail_fast_tracker.clone();
let specifier_options = options.specifier.clone(); let specifier_options = options.specifier.clone();
spawn_blocking(move || { spawn_blocking(move || {
create_and_run_current_thread(test_specifier( create_and_run_current_thread(test_specifier(
worker_factory, worker_factory,
permissions, permissions_container,
specifier, specifier,
worker_sender, worker_sender,
fail_fast_tracker, fail_fast_tracker,
@ -1739,9 +1743,7 @@ async fn fetch_specifiers_with_test_mode(
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for (specifier, mode) in &mut specifiers_with_mode { for (specifier, mode) in &mut specifiers_with_mode {
let file = file_fetcher let file = file_fetcher.fetch_bypass_permissions(specifier).await?;
.fetch(specifier, &PermissionsContainer::allow_all())
.await?;
let (media_type, _) = file.resolve_media_type_and_charset(); let (media_type, _) = file.resolve_media_type_and_charset();
if matches!(media_type, MediaType::Unknown | MediaType::Dts) { 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 // Various test files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one // `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable. // file would have impact on other files, which is undesirable.
let permissions = let permission_desc_parser = factory.permission_desc_parser()?;
Permissions::from_options(&cli_options.permissions_options()?)?; let permissions = Permissions::from_options(
permission_desc_parser.as_ref(),
&cli_options.permissions_options(),
)?;
let log_level = cli_options.log_level(); let log_level = cli_options.log_level();
let members_with_test_options = let members_with_test_options =
@ -1802,6 +1807,7 @@ pub async fn run_tests(
test_specifiers( test_specifiers(
worker_factory, worker_factory,
&permissions, &permissions,
permission_desc_parser,
specifiers_with_mode specifiers_with_mode
.into_iter() .into_iter()
.filter_map(|(s, m)| match m { .filter_map(|(s, m)| match m {
@ -1914,8 +1920,11 @@ pub async fn run_tests_with_watch(
.flatten() .flatten()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let permissions = let permission_desc_parser = factory.permission_desc_parser()?;
Permissions::from_options(&cli_options.permissions_options()?)?; let permissions = Permissions::from_options(
permission_desc_parser.as_ref(),
&cli_options.permissions_options(),
)?;
let graph = module_graph_creator let graph = module_graph_creator
.create_graph(graph_kind, test_modules) .create_graph(graph_kind, test_modules)
.await?; .await?;
@ -1969,6 +1978,7 @@ pub async fn run_tests_with_watch(
test_specifiers( test_specifiers(
worker_factory, worker_factory,
&permissions, &permissions,
permission_desc_parser,
specifiers_with_mode specifiers_with_mode
.into_iter() .into_iter()
.filter_map(|(s, m)| match m { .filter_map(|(s, m)| match m {

View file

@ -30,6 +30,7 @@ use deno_runtime::deno_web::BlobStore;
use deno_runtime::fmt_errors::format_js_error; use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::inspector_server::InspectorServer; use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::ops::worker_host::CreateWebWorkerCb; use deno_runtime::ops::worker_host::CreateWebWorkerCb;
use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::web_worker::WebWorker; use deno_runtime::web_worker::WebWorker;
use deno_runtime::web_worker::WebWorkerOptions; use deno_runtime::web_worker::WebWorkerOptions;
use deno_runtime::worker::MainWorker; use deno_runtime::worker::MainWorker;
@ -121,23 +122,24 @@ pub struct CliMainWorkerOptions {
} }
struct SharedWorkerState { struct SharedWorkerState {
options: CliMainWorkerOptions,
subcommand: DenoSubcommand,
storage_key_resolver: StorageKeyResolver,
npm_resolver: Arc<dyn CliNpmResolver>,
node_resolver: Arc<NodeResolver>,
blob_store: Arc<BlobStore>, blob_store: Arc<BlobStore>,
broadcast_channel: InMemoryBroadcastChannel, broadcast_channel: InMemoryBroadcastChannel,
shared_array_buffer_store: SharedArrayBufferStore, code_cache: Option<Arc<dyn code_cache::CodeCache>>,
compiled_wasm_module_store: CompiledWasmModuleStore, compiled_wasm_module_store: CompiledWasmModuleStore,
module_loader_factory: Box<dyn ModuleLoaderFactory>, feature_checker: Arc<FeatureChecker>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
fs: Arc<dyn deno_fs::FileSystem>, fs: Arc<dyn deno_fs::FileSystem>,
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>, maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
maybe_inspector_server: Option<Arc<InspectorServer>>, maybe_inspector_server: Option<Arc<InspectorServer>>,
maybe_lockfile: Option<Arc<CliLockfile>>, maybe_lockfile: Option<Arc<CliLockfile>>,
feature_checker: Arc<FeatureChecker>, module_loader_factory: Box<dyn ModuleLoaderFactory>,
code_cache: Option<Arc<dyn code_cache::CodeCache>>, 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 { impl SharedWorkerState {
@ -418,40 +420,42 @@ pub struct CliMainWorkerFactory {
impl CliMainWorkerFactory { impl CliMainWorkerFactory {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
storage_key_resolver: StorageKeyResolver,
subcommand: DenoSubcommand,
npm_resolver: Arc<dyn CliNpmResolver>,
node_resolver: Arc<NodeResolver>,
blob_store: Arc<BlobStore>, blob_store: Arc<BlobStore>,
module_loader_factory: Box<dyn ModuleLoaderFactory>, code_cache: Option<Arc<dyn code_cache::CodeCache>>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>, feature_checker: Arc<FeatureChecker>,
fs: Arc<dyn deno_fs::FileSystem>, fs: Arc<dyn deno_fs::FileSystem>,
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>, maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
maybe_inspector_server: Option<Arc<InspectorServer>>, maybe_inspector_server: Option<Arc<InspectorServer>>,
maybe_lockfile: Option<Arc<CliLockfile>>, maybe_lockfile: Option<Arc<CliLockfile>>,
feature_checker: Arc<FeatureChecker>, module_loader_factory: Box<dyn ModuleLoaderFactory>,
code_cache: Option<Arc<dyn code_cache::CodeCache>>, 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, options: CliMainWorkerOptions,
) -> Self { ) -> Self {
Self { Self {
shared: Arc::new(SharedWorkerState { shared: Arc::new(SharedWorkerState {
options,
subcommand,
storage_key_resolver,
npm_resolver,
node_resolver,
blob_store, blob_store,
broadcast_channel: Default::default(), broadcast_channel: Default::default(),
shared_array_buffer_store: Default::default(), code_cache,
compiled_wasm_module_store: Default::default(), compiled_wasm_module_store: Default::default(),
module_loader_factory, feature_checker,
root_cert_store_provider,
fs, fs,
maybe_file_watcher_communicator, maybe_file_watcher_communicator,
maybe_inspector_server, maybe_inspector_server,
maybe_lockfile, maybe_lockfile,
feature_checker, module_loader_factory,
code_cache, 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) (main_module, false)
}; };
let ModuleLoaderAndSourceMapGetter { module_loader } = shared let ModuleLoaderAndSourceMapGetter { module_loader } =
.module_loader_factory shared.module_loader_factory.create_for_main(
.create_for_main(PermissionsContainer::allow_all(), permissions.clone()); PermissionsContainer::allow_all(
self.shared.permission_desc_parser.clone(),
),
permissions.clone(),
);
let maybe_inspector_server = shared.maybe_inspector_server.clone(); let maybe_inspector_server = shared.maybe_inspector_server.clone();
let create_web_worker_cb = let create_web_worker_cb =
@ -619,6 +627,7 @@ impl CliMainWorkerFactory {
), ),
stdio, stdio,
feature_checker, feature_checker,
permission_desc_parser: shared.permission_desc_parser.clone(),
skip_op_registration: shared.options.skip_op_registration, skip_op_registration: shared.options.skip_op_registration,
v8_code_cache: shared.code_cache.clone(), v8_code_cache: shared.code_cache.clone(),
}; };
@ -809,6 +818,7 @@ fn create_web_worker_callback(
stdio: stdio.clone(), stdio: stdio.clone(),
cache_storage_dir, cache_storage_dir,
feature_checker, feature_checker,
permission_desc_parser: shared.permission_desc_parser.clone(),
strace_ops: shared.options.strace_ops.clone(), strace_ops: shared.options.strace_ops.clone(),
close_on_idle: args.close_on_idle, close_on_idle: args.close_on_idle,
maybe_worker_metadata: args.maybe_worker_metadata, maybe_worker_metadata: args.maybe_worker_metadata,
@ -830,13 +840,16 @@ fn create_web_worker_callback(
mod tests { mod tests {
use super::*; use super::*;
use deno_core::resolve_path; use deno_core::resolve_path;
use deno_fs::RealFs;
use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::Permissions;
fn create_test_worker() -> MainWorker { fn create_test_worker() -> MainWorker {
let main_module = let main_module =
resolve_path("./hello.js", &std::env::current_dir().unwrap()).unwrap(); resolve_path("./hello.js", &std::env::current_dir().unwrap()).unwrap();
let permissions = let permissions = PermissionsContainer::new(
PermissionsContainer::new(Permissions::none_without_prompt()); Arc::new(RuntimePermissionDescriptorParser::new(Arc::new(RealFs))),
Permissions::none_without_prompt(),
);
let options = WorkerOptions { let options = WorkerOptions {
startup_snapshot: crate::js::deno_isolate_init(), startup_snapshot: crate::js::deno_isolate_init(),

View file

@ -299,10 +299,15 @@ impl Drop for ResourceToBodyAdapter {
pub trait FetchPermissions { pub trait FetchPermissions {
fn check_net_url( fn check_net_url(
&mut self, &mut self,
_url: &Url, url: &Url,
api_name: &str, api_name: &str,
) -> Result<(), AnyError>; ) -> 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 { impl FetchPermissions for deno_permissions::PermissionsContainer {
@ -316,12 +321,16 @@ impl FetchPermissions for deno_permissions::PermissionsContainer {
} }
#[inline(always)] #[inline(always)]
fn check_read( fn check_read<'a>(
&mut self, &mut self,
path: &Path, path: &'a Path,
api_name: &str, api_name: &str,
) -> Result<(), AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
deno_permissions::PermissionsContainer::check_read(self, path, api_name) deno_permissions::PermissionsContainer::check_read_path(
self,
path,
Some(api_name),
)
} }
} }
@ -359,7 +368,11 @@ where
type_error("NetworkError when attempting to fetch resource") type_error("NetworkError when attempting to fetch resource")
})?; })?;
let permissions = state.borrow_mut::<FP>(); 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 { if method != Method::GET {
return Err(type_error(format!( return Err(type_error(format!(

View file

@ -287,7 +287,7 @@ where
{ {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
}; };
let symbol = PtrSymbol::new(pointer, &def)?; let symbol = PtrSymbol::new(pointer, &def)?;
@ -384,7 +384,7 @@ where
{ {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
}; };
let symbol = PtrSymbol::new(pointer, &def)?; let symbol = PtrSymbol::new(pointer, &def)?;

View file

@ -557,7 +557,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); 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 thread_id: u32 = LOCAL_THREAD_ID.with(|s| {
let value = *s.borrow(); let value = *s.borrow();

View file

@ -19,7 +19,6 @@ use serde_value::ValueDeserializer;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::c_void; use std::ffi::c_void;
use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
pub struct DynamicLibraryResource { pub struct DynamicLibraryResource {
@ -121,15 +120,13 @@ pub fn op_ffi_load<'scope, FP>(
where where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let path = args.path;
let permissions = state.borrow_mut::<FP>(); 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| { let lib = Library::open(&path).map_err(|e| {
dlopen2::Error::OpeningLibraryError(std::io::Error::new( dlopen2::Error::OpeningLibraryError(std::io::Error::new(
std::io::ErrorKind::Other, std::io::ErrorKind::Other,
format_error(e, path), format_error(e, &path),
)) ))
})?; })?;
let mut resource = DynamicLibraryResource { let mut resource = DynamicLibraryResource {
@ -290,7 +287,10 @@ fn sync_fn_impl<'s>(
// `path` is only used on Windows. // `path` is only used on Windows.
#[allow(unused_variables)] #[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 { match e {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
// This calls FormatMessageW with library path // 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 // https://github.com/denoland/deno/issues/11632
dlopen2::Error::OpeningLibraryError(e) => { dlopen2::Error::OpeningLibraryError(e) => {
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt; use std::os::windows::ffi::OsStrExt;
use winapi::shared::minwindef::DWORD; use winapi::shared::minwindef::DWORD;
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER; 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 mut buf = vec![0; 500];
let path = OsStr::new(&path) let path = path
.as_os_str()
.encode_wide() .encode_wide()
.chain(Some(0)) .chain(Some(0))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -384,7 +384,7 @@ mod tests {
std::io::Error::from_raw_os_error(0x000000C1), std::io::Error::from_raw_os_error(0x000000C1),
); );
assert_eq!( 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(), "foo.dll is not a valid Win32 application.\r\n".to_string(),
); );
} }

View file

@ -5,7 +5,7 @@ use deno_core::error::AnyError;
use std::mem::size_of; use std::mem::size_of;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::os::raw::c_short; use std::os::raw::c_short;
use std::path::Path; use std::path::PathBuf;
mod call; mod call;
mod callback; mod callback;
@ -41,13 +41,28 @@ const _: () = {
pub const UNSTABLE_FEATURE_NAME: &str = "ffi"; pub const UNSTABLE_FEATURE_NAME: &str = "ffi";
pub trait FfiPermissions { 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 { impl FfiPermissions for deno_permissions::PermissionsContainer {
#[inline(always)] #[inline(always)]
fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError> { fn check_partial_no_path(&mut self) -> Result<(), AnyError> {
deno_permissions::PermissionsContainer::check_ffi_partial(self, path) 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,
)
} }
} }

View file

@ -21,7 +21,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
Ok(ptr_number as *mut c_void) Ok(ptr_number as *mut c_void)
} }
@ -36,7 +36,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
Ok(a == b) Ok(a == b)
} }
@ -50,7 +50,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
Ok(buf as *mut c_void) Ok(buf as *mut c_void)
} }
@ -64,7 +64,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
let Some(buf) = buf.get_backing_store() else { let Some(buf) = buf.get_backing_store() else {
return Ok(0 as _); return Ok(0 as _);
@ -85,7 +85,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid pointer to offset, pointer is null")); return Err(type_error("Invalid pointer to offset, pointer is null"));
@ -115,7 +115,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
Ok(ptr as usize) Ok(ptr as usize)
} }
@ -132,7 +132,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid ArrayBuffer pointer, pointer is null")); return Err(type_error("Invalid ArrayBuffer pointer, pointer is null"));
@ -164,7 +164,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if src.is_null() { if src.is_null() {
Err(type_error("Invalid ArrayBuffer pointer, pointer is null")) Err(type_error("Invalid ArrayBuffer pointer, pointer is null"))
@ -195,7 +195,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid CString pointer, pointer is null")); return Err(type_error("Invalid CString pointer, pointer is null"));
@ -221,7 +221,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid bool pointer, pointer is null")); return Err(type_error("Invalid bool pointer, pointer is null"));
@ -241,7 +241,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid u8 pointer, pointer is null")); return Err(type_error("Invalid u8 pointer, pointer is null"));
@ -263,7 +263,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid i8 pointer, pointer is null")); return Err(type_error("Invalid i8 pointer, pointer is null"));
@ -285,7 +285,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid u16 pointer, pointer is null")); return Err(type_error("Invalid u16 pointer, pointer is null"));
@ -307,7 +307,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid i16 pointer, pointer is null")); return Err(type_error("Invalid i16 pointer, pointer is null"));
@ -329,7 +329,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid u32 pointer, pointer is null")); return Err(type_error("Invalid u32 pointer, pointer is null"));
@ -349,7 +349,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid i32 pointer, pointer is null")); return Err(type_error("Invalid i32 pointer, pointer is null"));
@ -372,7 +372,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid u64 pointer, pointer is null")); return Err(type_error("Invalid u64 pointer, pointer is null"));
@ -398,7 +398,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid i64 pointer, pointer is null")); return Err(type_error("Invalid i64 pointer, pointer is null"));
@ -421,7 +421,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid f32 pointer, pointer is null")); return Err(type_error("Invalid f32 pointer, pointer is null"));
@ -441,7 +441,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid f64 pointer, pointer is null")); return Err(type_error("Invalid f64 pointer, pointer is null"));
@ -461,7 +461,7 @@ where
FP: FfiPermissions + 'static, FP: FfiPermissions + 'static,
{ {
let permissions = state.borrow_mut::<FP>(); let permissions = state.borrow_mut::<FP>();
permissions.check_partial(None)?; permissions.check_partial_no_path()?;
if ptr.is_null() { if ptr.is_null() {
return Err(type_error("Invalid pointer pointer, pointer is null")); return Err(type_error("Invalid pointer pointer, pointer is null"));

View file

@ -24,6 +24,7 @@ use deno_core::error::AnyError;
use deno_io::fs::FsError; use deno_io::fs::FsError;
use std::borrow::Cow; use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
pub trait FsPermissions { pub trait FsPermissions {
fn check_open<'a>( fn check_open<'a>(
@ -34,8 +35,18 @@ pub trait FsPermissions {
path: &'a Path, path: &'a Path,
api_name: &str, api_name: &str,
) -> Result<std::borrow::Cow<'a, Path>, FsError>; ) -> Result<std::borrow::Cow<'a, Path>, FsError>;
fn check_read(&mut self, path: &Path, api_name: &str) #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
-> Result<(), AnyError>; 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_all(&mut self, api_name: &str) -> Result<(), AnyError>;
fn check_read_blind( fn check_read_blind(
&mut self, &mut self,
@ -43,16 +54,24 @@ pub trait FsPermissions {
display: &str, display: &str,
api_name: &str, api_name: &str,
) -> Result<(), AnyError>; ) -> Result<(), AnyError>;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_write( fn check_write(
&mut self, &mut self,
path: &Path, path: &str,
api_name: &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( fn check_write_partial(
&mut self, &mut self,
path: &Path, path: &str,
api_name: &str, api_name: &str,
) -> Result<(), AnyError>; ) -> Result<PathBuf, AnyError>;
fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError>; fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError>;
fn check_write_blind( fn check_write_blind(
&mut self, &mut self,
@ -96,25 +115,44 @@ impl FsPermissions for deno_permissions::PermissionsContainer {
// If somehow read or write aren't specified, use read // If somehow read or write aren't specified, use read
let read = read || !write; let read = read || !write;
let mut path: Cow<'a, Path> = Cow::Borrowed(path);
if read { 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"))?; .map_err(|_| FsError::NotCapable("read"))?;
if let Cow::Owned(resolved_path) = resolved_path {
path = Cow::Owned(resolved_path);
}
} }
if write { if write {
FsPermissions::check_write(self, path, api_name) let resolved_path =
FsPermissions::check_write_path(self, &path, api_name)
.map_err(|_| FsError::NotCapable("write"))?; .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( fn check_read(
&mut self, &mut self,
path: &Path, path: &str,
api_name: &str, api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read(self, path, api_name) 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( fn check_read_blind(
&mut self, &mut self,
path: &Path, path: &Path,
@ -128,17 +166,27 @@ impl FsPermissions for deno_permissions::PermissionsContainer {
fn check_write( fn check_write(
&mut self, &mut self,
path: &Path, path: &str,
api_name: &str, api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write(self, path, api_name) 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( fn check_write_partial(
&mut self, &mut self,
path: &Path, path: &str,
api_name: &str, api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write_partial( deno_permissions::PermissionsContainer::check_write_partial(
self, path, api_name, self, path, api_name,
) )

View file

@ -5,6 +5,7 @@ use std::io;
use std::io::SeekFrom; use std::io::SeekFrom;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::path::StripPrefixError;
use std::rc::Rc; use std::rc::Rc;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
@ -105,8 +106,9 @@ pub fn op_fs_chdir<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let d = PathBuf::from(&directory); let d = state
state.borrow_mut::<P>().check_read(&d, "Deno.chdir()")?; .borrow_mut::<P>()
.check_read(directory, "Deno.chdir()")?;
state state
.borrow::<FileSystemRc>() .borrow::<FileSystemRc>()
.chdir(&d) .chdir(&d)
@ -188,11 +190,9 @@ pub fn op_fs_mkdir_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path);
let mode = mode.unwrap_or(0o777) & 0o777; let mode = mode.unwrap_or(0o777) & 0o777;
state let path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write(&path, "Deno.mkdirSync()")?; .check_write(&path, "Deno.mkdirSync()")?;
@ -213,14 +213,12 @@ pub async fn op_fs_mkdir_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path);
let mode = mode.unwrap_or(0o777) & 0o777; let mode = mode.unwrap_or(0o777) & 0o777;
let fs = { let (fs, path) = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?; let path = state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), path)
}; };
fs.mkdir_async(path.clone(), recursive, mode) fs.mkdir_async(path.clone(), recursive, mode)
@ -239,8 +237,7 @@ pub fn op_fs_chmod_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write(&path, "Deno.chmodSync()")?; .check_write(&path, "Deno.chmodSync()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
@ -257,11 +254,10 @@ pub async fn op_fs_chmod_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?; let path = state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), path)
}; };
fs.chmod_async(path.clone(), mode) fs.chmod_async(path.clone(), mode)
.await .await
@ -279,8 +275,7 @@ pub fn op_fs_chown_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write(&path, "Deno.chownSync()")?; .check_write(&path, "Deno.chownSync()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
@ -299,11 +294,10 @@ pub async fn op_fs_chown_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?; let path = state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), path)
}; };
fs.chown_async(path.clone(), uid, gid) fs.chown_async(path.clone(), uid, gid)
.await .await
@ -320,11 +314,9 @@ pub fn op_fs_remove_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write(&path, "Deno.removeSync()")?; .check_write(path, "Deno.removeSync()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
fs.remove_sync(&path, recursive) fs.remove_sync(&path, recursive)
@ -342,21 +334,19 @@ pub async fn op_fs_remove_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
if recursive { let path = if recursive {
state state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write(&path, "Deno.remove()")?; .check_write(&path, "Deno.remove()")?
} else { } else {
state state
.borrow_mut::<P>() .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) fs.remove_async(path.clone(), recursive)
@ -375,12 +365,9 @@ pub fn op_fs_copy_file_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let from = PathBuf::from(from);
let to = PathBuf::from(to);
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&from, "Deno.copyFileSync()")?; let from = permissions.check_read(from, "Deno.copyFileSync()")?;
permissions.check_write(&to, "Deno.copyFileSync()")?; let to = permissions.check_write(to, "Deno.copyFileSync()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
fs.copy_file_sync(&from, &to) fs.copy_file_sync(&from, &to)
@ -398,15 +385,12 @@ pub async fn op_fs_copy_file_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let from = PathBuf::from(from); let (fs, from, to) = {
let to = PathBuf::from(to);
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&from, "Deno.copyFile()")?; let from = permissions.check_read(&from, "Deno.copyFile()")?;
permissions.check_write(&to, "Deno.copyFile()")?; let to = permissions.check_write(&to, "Deno.copyFile()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), from, to)
}; };
fs.copy_file_async(from.clone(), to.clone()) fs.copy_file_async(from.clone(), to.clone())
@ -425,8 +409,7 @@ pub fn op_fs_stat_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read(&path, "Deno.statSync()")?; .check_read(&path, "Deno.statSync()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
@ -445,12 +428,11 @@ pub async fn op_fs_stat_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&path, "Deno.stat()")?; let path = permissions.check_read(&path, "Deno.stat()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), path)
}; };
let stat = fs let stat = fs
.stat_async(path.clone()) .stat_async(path.clone())
@ -468,8 +450,7 @@ pub fn op_fs_lstat_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read(&path, "Deno.lstatSync()")?; .check_read(&path, "Deno.lstatSync()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
@ -488,12 +469,11 @@ pub async fn op_fs_lstat_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&path, "Deno.lstat()")?; let path = permissions.check_read(&path, "Deno.lstat()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), path)
}; };
let stat = fs let stat = fs
.lstat_async(path.clone()) .lstat_async(path.clone())
@ -511,11 +491,9 @@ pub fn op_fs_realpath_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path);
let fs = state.borrow::<FileSystemRc>().clone(); let fs = state.borrow::<FileSystemRc>().clone();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&path, "Deno.realPathSync()")?; let path = permissions.check_read(&path, "Deno.realPathSync()")?;
if path.is_relative() { if path.is_relative() {
permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPathSync()")?; permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPathSync()")?;
} }
@ -536,18 +514,16 @@ pub async fn op_fs_realpath_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs;
{
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
fs = state.borrow::<FileSystemRc>().clone(); let fs = state.borrow::<FileSystemRc>().clone();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&path, "Deno.realPath()")?; let path = permissions.check_read(&path, "Deno.realPath()")?;
if path.is_relative() { if path.is_relative() {
permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPath()")?; permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPath()")?;
} }
} (fs, path)
};
let resolved_path = fs let resolved_path = fs
.realpath_async(path.clone()) .realpath_async(path.clone())
.await .await
@ -566,9 +542,7 @@ pub fn op_fs_read_dir_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read(&path, "Deno.readDirSync()")?; .check_read(&path, "Deno.readDirSync()")?;
@ -587,14 +561,12 @@ pub async fn op_fs_read_dir_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state let path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read(&path, "Deno.readDir()")?; .check_read(&path, "Deno.readDir()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), path)
}; };
let entries = fs let entries = fs
@ -614,13 +586,10 @@ pub fn op_fs_rename_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let oldpath = PathBuf::from(oldpath);
let newpath = PathBuf::from(newpath);
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&oldpath, "Deno.renameSync()")?; let _ = permissions.check_read(&oldpath, "Deno.renameSync()")?;
permissions.check_write(&oldpath, "Deno.renameSync()")?; let oldpath = permissions.check_write(&oldpath, "Deno.renameSync()")?;
permissions.check_write(&newpath, "Deno.renameSync()")?; let newpath = permissions.check_write(&newpath, "Deno.renameSync()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
fs.rename_sync(&oldpath, &newpath) fs.rename_sync(&oldpath, &newpath)
@ -638,16 +607,13 @@ pub async fn op_fs_rename_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let oldpath = PathBuf::from(oldpath); let (fs, oldpath, newpath) = {
let newpath = PathBuf::from(newpath);
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&oldpath, "Deno.rename()")?; _ = permissions.check_read(&oldpath, "Deno.rename()")?;
permissions.check_write(&oldpath, "Deno.rename()")?; let oldpath = permissions.check_write(&oldpath, "Deno.rename()")?;
permissions.check_write(&newpath, "Deno.rename()")?; let newpath = permissions.check_write(&newpath, "Deno.rename()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), oldpath, newpath)
}; };
fs.rename_async(oldpath.clone(), newpath.clone()) fs.rename_async(oldpath.clone(), newpath.clone())
@ -666,14 +632,11 @@ pub fn op_fs_link_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let oldpath = PathBuf::from(oldpath);
let newpath = PathBuf::from(newpath);
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&oldpath, "Deno.linkSync()")?; _ = permissions.check_read(oldpath, "Deno.linkSync()")?;
permissions.check_write(&oldpath, "Deno.linkSync()")?; let oldpath = permissions.check_write(oldpath, "Deno.linkSync()")?;
permissions.check_read(&newpath, "Deno.linkSync()")?; _ = permissions.check_read(newpath, "Deno.linkSync()")?;
permissions.check_write(&newpath, "Deno.linkSync()")?; let newpath = permissions.check_write(newpath, "Deno.linkSync()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
fs.link_sync(&oldpath, &newpath) fs.link_sync(&oldpath, &newpath)
@ -691,17 +654,14 @@ pub async fn op_fs_link_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let oldpath = PathBuf::from(&oldpath); let (fs, oldpath, newpath) = {
let newpath = PathBuf::from(&newpath);
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(&oldpath, "Deno.link()")?; _ = permissions.check_read(&oldpath, "Deno.link()")?;
permissions.check_write(&oldpath, "Deno.link()")?; let oldpath = permissions.check_write(&oldpath, "Deno.link()")?;
permissions.check_read(&newpath, "Deno.link()")?; _ = permissions.check_read(&newpath, "Deno.link()")?;
permissions.check_write(&newpath, "Deno.link()")?; let newpath = permissions.check_write(&newpath, "Deno.link()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), oldpath, newpath)
}; };
fs.link_async(oldpath.clone(), newpath.clone()) fs.link_async(oldpath.clone(), newpath.clone())
@ -772,9 +732,7 @@ pub fn op_fs_read_link_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read(&path, "Deno.readLink()")?; .check_read(&path, "Deno.readLink()")?;
@ -794,14 +752,12 @@ pub async fn op_fs_read_link_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state let path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read(&path, "Deno.readLink()")?; .check_read(&path, "Deno.readLink()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), path)
}; };
let target = fs let target = fs
@ -821,11 +777,9 @@ pub fn op_fs_truncate_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write(&path, "Deno.truncateSync()")?; .check_write(path, "Deno.truncateSync()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
fs.truncate_sync(&path, len) fs.truncate_sync(&path, len)
@ -843,14 +797,12 @@ pub async fn op_fs_truncate_async<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state let path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write(&path, "Deno.truncate()")?; .check_write(&path, "Deno.truncate()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), path)
}; };
fs.truncate_async(path.clone(), len) fs.truncate_async(path.clone(), len)
@ -872,9 +824,7 @@ pub fn op_fs_utime_sync<P>(
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state.borrow_mut::<P>().check_write(path, "Deno.utime()")?;
state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
fs.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) 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 where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?; let path = state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
state.borrow::<FileSystemRc>().clone() (state.borrow::<FileSystemRc>().clone(), path)
}; };
fs.utime_async( fs.utime_async(
@ -920,15 +868,18 @@ where
#[string] #[string]
pub fn op_fs_make_temp_dir_sync<P>( pub fn op_fs_make_temp_dir_sync<P>(
state: &mut OpState, state: &mut OpState,
#[string] dir: Option<String>, #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>, #[string] prefix: Option<String>,
#[string] suffix: Option<String>, #[string] suffix: Option<String>,
) -> Result<String, AnyError> ) -> Result<String, AnyError>
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let (dir, fs) = let (dir, fs) = make_temp_check_sync::<P>(
make_temp_check_sync::<P>(state, dir, "Deno.makeTempDirSync()")?; state,
dir_arg.as_deref(),
"Deno.makeTempDirSync()",
)?;
let mut rng = thread_rng(); let mut rng = thread_rng();
@ -936,7 +887,11 @@ where
for _ in 0..MAX_TRIES { for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?; let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.mkdir_sync(&path, false, 0o700) { 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 => { Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue; continue;
} }
@ -955,14 +910,18 @@ where
#[string] #[string]
pub async fn op_fs_make_temp_dir_async<P>( pub async fn op_fs_make_temp_dir_async<P>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[string] dir: Option<String>, #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>, #[string] prefix: Option<String>,
#[string] suffix: Option<String>, #[string] suffix: Option<String>,
) -> Result<String, AnyError> ) -> Result<String, AnyError>
where where
P: FsPermissions + 'static, 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(); let mut rng = thread_rng();
@ -970,7 +929,11 @@ where
for _ in 0..MAX_TRIES { for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?; let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.clone().mkdir_async(path.clone(), false, 0o700).await { 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 => { Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue; continue;
} }
@ -989,15 +952,18 @@ where
#[string] #[string]
pub fn op_fs_make_temp_file_sync<P>( pub fn op_fs_make_temp_file_sync<P>(
state: &mut OpState, state: &mut OpState,
#[string] dir: Option<String>, #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>, #[string] prefix: Option<String>,
#[string] suffix: Option<String>, #[string] suffix: Option<String>,
) -> Result<String, AnyError> ) -> Result<String, AnyError>
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let (dir, fs) = let (dir, fs) = make_temp_check_sync::<P>(
make_temp_check_sync::<P>(state, dir, "Deno.makeTempFileSync()")?; state,
dir_arg.as_deref(),
"Deno.makeTempFileSync()",
)?;
let open_opts = OpenOptions { let open_opts = OpenOptions {
write: true, write: true,
@ -1011,7 +977,11 @@ where
for _ in 0..MAX_TRIES { for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?; let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.open_sync(&path, open_opts, None) { 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 => { Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue; continue;
} }
@ -1030,15 +1000,18 @@ where
#[string] #[string]
pub async fn op_fs_make_temp_file_async<P>( pub async fn op_fs_make_temp_file_async<P>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[string] dir: Option<String>, #[string] dir_arg: Option<String>,
#[string] prefix: Option<String>, #[string] prefix: Option<String>,
#[string] suffix: Option<String>, #[string] suffix: Option<String>,
) -> Result<String, AnyError> ) -> Result<String, AnyError>
where where
P: FsPermissions + 'static, P: FsPermissions + 'static,
{ {
let (dir, fs) = let (dir, fs) = make_temp_check_async::<P>(
make_temp_check_async::<P>(state, dir, "Deno.makeTempFile()")?; state,
dir_arg.as_deref(),
"Deno.makeTempFile()",
)?;
let open_opts = OpenOptions { let open_opts = OpenOptions {
write: true, write: true,
@ -1053,7 +1026,11 @@ where
for _ in 0..MAX_TRIES { for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?; let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
match fs.clone().open_async(path.clone(), open_opts, None).await { 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 => { Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue; continue;
} }
@ -1067,9 +1044,26 @@ where
.context("tmpfile") .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>( fn make_temp_check_sync<P>(
state: &mut OpState, state: &mut OpState,
dir: Option<String>, dir: Option<&str>,
api_name: &str, api_name: &str,
) -> Result<(PathBuf, FileSystemRc), AnyError> ) -> Result<(PathBuf, FileSystemRc), AnyError>
where where
@ -1077,11 +1071,7 @@ where
{ {
let fs = state.borrow::<FileSystemRc>().clone(); let fs = state.borrow::<FileSystemRc>().clone();
let dir = match dir { let dir = match dir {
Some(dir) => { Some(dir) => state.borrow_mut::<P>().check_write(dir, api_name)?,
let dir = PathBuf::from(dir);
state.borrow_mut::<P>().check_write(&dir, api_name)?;
dir
}
None => { None => {
let dir = fs.tmp_dir().context("tmpdir")?; let dir = fs.tmp_dir().context("tmpdir")?;
state state
@ -1095,7 +1085,7 @@ where
fn make_temp_check_async<P>( fn make_temp_check_async<P>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
dir: Option<String>, dir: Option<&str>,
api_name: &str, api_name: &str,
) -> Result<(PathBuf, FileSystemRc), AnyError> ) -> Result<(PathBuf, FileSystemRc), AnyError>
where where
@ -1104,11 +1094,7 @@ where
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let fs = state.borrow::<FileSystemRc>().clone(); let fs = state.borrow::<FileSystemRc>().clone();
let dir = match dir { let dir = match dir {
Some(dir) => { Some(dir) => state.borrow_mut::<P>().check_write(dir, api_name)?,
let dir = PathBuf::from(dir);
state.borrow_mut::<P>().check_write(&dir, api_name)?;
dir
}
None => { None => {
let dir = fs.tmp_dir().context("tmpdir")?; let dir = fs.tmp_dir().context("tmpdir")?;
state state

View file

@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::env::current_dir; use std::env::current_dir;
@ -36,19 +37,37 @@ pub struct SqliteDbHandler<P: SqliteDbHandlerPermissions + 'static> {
} }
pub trait SqliteDbHandlerPermissions { pub trait SqliteDbHandlerPermissions {
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_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>; 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 { impl SqliteDbHandlerPermissions for deno_permissions::PermissionsContainer {
#[inline(always)] #[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) deno_permissions::PermissionsContainer::check_read(self, p, api_name)
} }
#[inline(always)] #[inline(always)]
fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError> { fn check_write<'a>(
deno_permissions::PermissionsContainer::check_write(self, p, api_name) &mut self,
p: &'a Path,
api_name: &str,
) -> Result<Cow<'a, Path>, AnyError> {
deno_permissions::PermissionsContainer::check_write_path(self, p, api_name)
} }
} }
@ -74,9 +93,17 @@ impl<P: SqliteDbHandlerPermissions> DatabaseHandler for SqliteDbHandler<P> {
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
path: Option<String>, path: Option<String>,
) -> Result<Self::DB, AnyError> { ) -> Result<Self::DB, AnyError> {
// Validate path #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
if let Some(path) = &path { fn validate_path<P: SqliteDbHandlerPermissions + 'static>(
if path != ":memory:" { 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() { if path.is_empty() {
return Err(type_error("Filename cannot be empty")); return Err(type_error("Filename cannot be empty"));
} }
@ -85,17 +112,16 @@ impl<P: SqliteDbHandlerPermissions> DatabaseHandler for SqliteDbHandler<P> {
"Filename cannot start with ':' unless prefixed with './'", "Filename cannot start with ':' unless prefixed with './'",
)); ));
} }
let path = Path::new(path);
{ {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
permissions.check_read(path, "Deno.openKv")?; let path = permissions.check_read(&path, "Deno.openKv")?;
permissions.check_write(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(); let default_storage_dir = self.default_storage_dir.clone();
type ConnGen = type ConnGen =
Arc<dyn Fn() -> rusqlite::Result<rusqlite::Connection> + Send + Sync>; Arc<dyn Fn() -> rusqlite::Result<rusqlite::Connection> + Send + Sync>;

View file

@ -16,7 +16,6 @@ use deno_core::OpState;
use deno_core::V8CrossThreadTaskSpawner; use deno_core::V8CrossThreadTaskSpawner;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use std::thread_local; use std::thread_local;
@ -482,15 +481,15 @@ deno_core::extension!(deno_napi,
); );
pub trait NapiPermissions { pub trait NapiPermissions {
fn check(&mut self, path: Option<&Path>) #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
-> std::result::Result<(), AnyError>; fn check(&mut self, path: &str) -> std::result::Result<PathBuf, AnyError>;
} }
// NOTE(bartlomieju): for now, NAPI uses `--allow-ffi` flag, but that might // NOTE(bartlomieju): for now, NAPI uses `--allow-ffi` flag, but that might
// change in the future. // change in the future.
impl NapiPermissions for deno_permissions::PermissionsContainer { impl NapiPermissions for deno_permissions::PermissionsContainer {
#[inline(always)] #[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) deno_permissions::PermissionsContainer::check_ffi(self, path)
} }
} }
@ -501,7 +500,7 @@ unsafe impl Send for NapiModuleHandle {}
struct NapiModuleHandle(*const NapiModule); struct NapiModuleHandle(*const NapiModule);
static NAPI_LOADED_MODULES: std::sync::LazyLock< static NAPI_LOADED_MODULES: std::sync::LazyLock<
RwLock<HashMap<String, NapiModuleHandle>>, RwLock<HashMap<PathBuf, NapiModuleHandle>>,
> = std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); > = std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
#[op2(reentrant)] #[op2(reentrant)]
@ -519,15 +518,16 @@ where
{ {
// We must limit the OpState borrow because this function can trigger a // We must limit the OpState borrow because this function can trigger a
// re-borrow through the NAPI module. // 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 mut op_state = op_state.borrow_mut();
let permissions = op_state.borrow_mut::<NP>(); 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>(); let napi_state = op_state.borrow::<NapiState>();
( (
op_state.borrow::<V8CrossThreadTaskSpawner>().clone(), op_state.borrow::<V8CrossThreadTaskSpawner>().clone(),
napi_state.env_cleanup_hooks.clone(), napi_state.env_cleanup_hooks.clone(),
op_state.external_ops_tracker.clone(), op_state.external_ops_tracker.clone(),
path,
) )
}; };
@ -612,7 +612,7 @@ where
} else { } else {
return Err(type_error(format!( return Err(type_error(format!(
"Unable to find register Node-API module at {}", "Unable to find register Node-API module at {}",
path path.display()
))); )));
}; };

View file

@ -13,6 +13,7 @@ use deno_core::error::AnyError;
use deno_core::OpState; use deno_core::OpState;
use deno_tls::rustls::RootCertStore; use deno_tls::rustls::RootCertStore;
use deno_tls::RootCertStoreProvider; use deno_tls::RootCertStoreProvider;
use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
@ -22,12 +23,27 @@ pub const UNSTABLE_FEATURE_NAME: &str = "net";
pub trait NetPermissions { pub trait NetPermissions {
fn check_net<T: AsRef<str>>( fn check_net<T: AsRef<str>>(
&mut self, &mut self,
_host: &(T, Option<u16>), host: &(T, Option<u16>),
_api_name: &str, api_name: &str,
) -> Result<(), AnyError>; ) -> 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_write(&mut self, _p: &Path, _api_name: &str) fn check_read(
-> Result<(), AnyError>; &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 { impl NetPermissions for deno_permissions::PermissionsContainer {
@ -43,20 +59,31 @@ impl NetPermissions for deno_permissions::PermissionsContainer {
#[inline(always)] #[inline(always)]
fn check_read( fn check_read(
&mut self, &mut self,
path: &Path, path: &str,
api_name: &str, api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read(self, path, api_name) deno_permissions::PermissionsContainer::check_read(self, path, api_name)
} }
#[inline(always)] #[inline(always)]
fn check_write( fn check_write(
&mut self, &mut self,
path: &Path, path: &str,
api_name: &str, api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write(self, path, api_name) 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. /// Helper for checking unstable features. Used for sync ops.

View file

@ -784,6 +784,7 @@ mod tests {
use std::net::Ipv6Addr; use std::net::Ipv6Addr;
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use trust_dns_proto::rr::rdata::a::A; use trust_dns_proto::rr::rdata::a::A;
@ -991,18 +992,26 @@ mod tests {
fn check_read( fn check_read(
&mut self, &mut self,
_p: &Path, p: &str,
_api_name: &str, _api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
Ok(()) Ok(PathBuf::from(p))
} }
fn check_write( fn check_write(
&mut self, &mut self,
_p: &Path, p: &str,
_api_name: &str, _api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
Ok(()) 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))
} }
} }

View file

@ -52,7 +52,6 @@ use std::io::ErrorKind;
use std::io::Read; use std::io::Read;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
@ -356,15 +355,17 @@ where
.try_borrow::<UnsafelyIgnoreCertificateErrors>() .try_borrow::<UnsafelyIgnoreCertificateErrors>()
.and_then(|it| it.0.clone()); .and_then(|it| it.0.clone());
{ let cert_file = {
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
let permissions = s.borrow_mut::<NP>(); let permissions = s.borrow_mut::<NP>();
permissions permissions
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectTls()")?; .check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectTls()")?;
if let Some(path) = cert_file { 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 let mut ca_certs = args
.ca_certs .ca_certs

View file

@ -94,22 +94,22 @@ pub async fn op_net_accept_unix(
#[serde] #[serde]
pub async fn op_net_connect_unix<NP>( pub async fn op_net_connect_unix<NP>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[string] path: String, #[string] address_path: String,
) -> Result<(ResourceId, Option<String>, Option<String>), AnyError> ) -> Result<(ResourceId, Option<String>, Option<String>), AnyError>
where where
NP: NetPermissions + 'static, NP: NetPermissions + 'static,
{ {
let address_path = Path::new(&path); let address_path = {
{
let mut state_ = state.borrow_mut(); let mut state_ = state.borrow_mut();
state_ let address_path = state_
.borrow_mut::<NP>() .borrow_mut::<NP>()
.check_read(address_path, "Deno.connect()")?; .check_read(&address_path, "Deno.connect()")?;
state_ _ = state_
.borrow_mut::<NP>() .borrow_mut::<NP>()
.check_write(address_path, "Deno.connect()")?; .check_write_path(&address_path, "Deno.connect()")?;
} address_path
let unix_stream = UnixStream::connect(Path::new(&path)).await?; };
let unix_stream = UnixStream::connect(&address_path).await?;
let local_addr = unix_stream.local_addr()?; let local_addr = unix_stream.local_addr()?;
let remote_addr = unix_stream.peer_addr()?; let remote_addr = unix_stream.peer_addr()?;
let local_addr_path = local_addr.as_pathname().map(pathstring).transpose()?; 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>( pub async fn op_net_send_unixpacket<NP>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId, #[smi] rid: ResourceId,
#[string] path: String, #[string] address_path: String,
#[buffer] zero_copy: JsBuffer, #[buffer] zero_copy: JsBuffer,
) -> Result<usize, AnyError> ) -> Result<usize, AnyError>
where where
NP: NetPermissions + 'static, NP: NetPermissions + 'static,
{ {
let address_path = Path::new(&path); let address_path = {
{
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
s.borrow_mut::<NP>() s.borrow_mut::<NP>()
.check_write(address_path, "Deno.DatagramConn.send()")?; .check_write(&address_path, "Deno.DatagramConn.send()")?
} };
let resource = state let resource = state
.borrow() .borrow()
@ -178,17 +177,16 @@ where
#[serde] #[serde]
pub fn op_net_listen_unix<NP>( pub fn op_net_listen_unix<NP>(
state: &mut OpState, state: &mut OpState,
#[string] path: String, #[string] address_path: String,
#[string] api_name: String, #[string] api_name: String,
) -> Result<(ResourceId, Option<String>), AnyError> ) -> Result<(ResourceId, Option<String>), AnyError>
where where
NP: NetPermissions + 'static, NP: NetPermissions + 'static,
{ {
let address_path = Path::new(&path);
let permissions = state.borrow_mut::<NP>(); let permissions = state.borrow_mut::<NP>();
let api_call_expr = format!("{}()", api_name); let api_call_expr = format!("{}()", api_name);
permissions.check_read(address_path, &api_call_expr)?; let address_path = permissions.check_read(&address_path, &api_call_expr)?;
permissions.check_write(address_path, &api_call_expr)?; _ = permissions.check_write_path(&address_path, &api_call_expr)?;
let listener = UnixListener::bind(address_path)?; let listener = UnixListener::bind(address_path)?;
let local_addr = listener.local_addr()?; let local_addr = listener.local_addr()?;
let pathname = local_addr.as_pathname().map(pathstring).transpose()?; let pathname = local_addr.as_pathname().map(pathstring).transpose()?;
@ -199,15 +197,15 @@ where
pub fn net_listen_unixpacket<NP>( pub fn net_listen_unixpacket<NP>(
state: &mut OpState, state: &mut OpState,
path: String, address_path: String,
) -> Result<(ResourceId, Option<String>), AnyError> ) -> Result<(ResourceId, Option<String>), AnyError>
where where
NP: NetPermissions + 'static, NP: NetPermissions + 'static,
{ {
let address_path = Path::new(&path);
let permissions = state.borrow_mut::<NP>(); let permissions = state.borrow_mut::<NP>();
permissions.check_read(address_path, "Deno.listenDatagram()")?; let address_path =
permissions.check_write(address_path, "Deno.listenDatagram()")?; permissions.check_read(&address_path, "Deno.listenDatagram()")?;
_ = permissions.check_write_path(&address_path, "Deno.listenDatagram()")?;
let socket = UnixDatagram::bind(address_path)?; let socket = UnixDatagram::bind(address_path)?;
let local_addr = socket.local_addr()?; let local_addr = socket.local_addr()?;
let pathname = local_addr.as_pathname().map(pathstring).transpose()?; let pathname = local_addr.as_pathname().map(pathstring).transpose()?;

View file

@ -3,8 +3,10 @@
#![deny(clippy::print_stderr)] #![deny(clippy::print_stderr)]
#![deny(clippy::print_stdout)] #![deny(clippy::print_stdout)]
use std::borrow::Cow;
use std::collections::HashSet; use std::collections::HashSet;
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::located_script_name; use deno_core::located_script_name;
@ -49,21 +51,29 @@ pub trait NodePermissions {
url: &Url, url: &Url,
api_name: &str, api_name: &str,
) -> Result<(), AnyError>; ) -> Result<(), AnyError>;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)] #[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) 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( fn check_read_with_api_name(
&mut self, &mut self,
path: &Path, path: &str,
api_name: Option<&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>; 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( fn check_write_with_api_name(
&mut self, &mut self,
path: &Path, path: &str,
api_name: Option<&str>, api_name: Option<&str>,
) -> Result<(), AnyError>; ) -> Result<PathBuf, AnyError>;
} }
impl NodePermissions for deno_permissions::PermissionsContainer { impl NodePermissions for deno_permissions::PermissionsContainer {
@ -79,20 +89,27 @@ impl NodePermissions for deno_permissions::PermissionsContainer {
#[inline(always)] #[inline(always)]
fn check_read_with_api_name( fn check_read_with_api_name(
&mut self, &mut self,
path: &Path, path: &str,
api_name: Option<&str>, api_name: Option<&str>,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_read_with_api_name( deno_permissions::PermissionsContainer::check_read_with_api_name(
self, path, 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)] #[inline(always)]
fn check_write_with_api_name( fn check_write_with_api_name(
&mut self, &mut self,
path: &Path, path: &str,
api_name: Option<&str>, api_name: Option<&str>,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_write_with_api_name( deno_permissions::PermissionsContainer::check_write_with_api_name(
self, path, api_name, self, path, api_name,
) )

View file

@ -1,8 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::cell::RefCell; use std::cell::RefCell;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use deno_core::error::AnyError; use deno_core::error::AnyError;
@ -21,8 +19,7 @@ pub fn op_node_fs_exists_sync<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.existsSync()"))?; .check_read_with_api_name(&path, Some("node:fs.existsSync()"))?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
@ -37,14 +34,12 @@ pub async fn op_node_fs_exists<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state let path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.exists()"))?; .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?) Ok(fs.exists_async(path).await?)
@ -59,18 +54,15 @@ pub fn op_node_cp_sync<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = Path::new(path); let path = state
let new_path = Path::new(new_path);
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read_with_api_name(path, Some("node:fs.cpSync"))?; .check_read_with_api_name(path, Some("node:fs.cpSync"))?;
state let new_path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write_with_api_name(new_path, Some("node:fs.cpSync"))?; .check_write_with_api_name(new_path, Some("node:fs.cpSync"))?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
fs.cp_sync(path, new_path)?; fs.cp_sync(&path, &new_path)?;
Ok(()) Ok(())
} }
@ -83,18 +75,15 @@ pub async fn op_node_cp<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path, new_path) = {
let new_path = PathBuf::from(new_path);
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state let path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.cpSync"))?; .check_read_with_api_name(&path, Some("node:fs.cpSync"))?;
state let new_path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write_with_api_name(&new_path, Some("node:fs.cpSync"))?; .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?; fs.cp_async(path, new_path).await?;
@ -123,21 +112,21 @@ pub fn op_node_statfs<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
{ let path = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state let path = state
.borrow_mut::<P>() .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 state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_sys("statfs", "node:fs.statfs")?; .check_sys("statfs", "node:fs.statfs")?;
} path
};
#[cfg(unix)] #[cfg(unix)]
{ {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt; 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(); let mut cpath = path.as_bytes().to_vec();
cpath.push(0); cpath.push(0);
if bigint { if bigint {
@ -196,7 +185,7 @@ where
// Using a vfs here doesn't make sense, it won't align with the windows API // Using a vfs here doesn't make sense, it won't align with the windows API
// call below. // call below.
#[allow(clippy::disallowed_methods)] #[allow(clippy::disallowed_methods)]
let path = Path::new(&path).canonicalize()?; let path = path.canonicalize()?;
let root = path let root = path
.ancestors() .ancestors()
.last() .last()
@ -256,14 +245,12 @@ pub fn op_node_lutimes_sync<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = Path::new(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write_with_api_name(path, Some("node:fs.lutimes"))?; .check_write_with_api_name(path, Some("node:fs.lutimes"))?;
let fs = state.borrow::<FileSystemRc>(); 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(()) Ok(())
} }
@ -279,14 +266,12 @@ pub async fn op_node_lutimes<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state let path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lutimesSync"))?; .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) fs.lutime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
@ -305,8 +290,7 @@ pub fn op_node_lchown_sync<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lchownSync"))?; .check_write_with_api_name(&path, Some("node:fs.lchownSync"))?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
@ -324,13 +308,12 @@ pub async fn op_node_lchown<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let path = PathBuf::from(path); let (fs, path) = {
let fs = {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state let path = state
.borrow_mut::<P>() .borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lchown"))?; .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?; fs.lchown_async(path, uid, gid).await?;
Ok(()) Ok(())

View file

@ -5,12 +5,15 @@
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::op2; use deno_core::op2;
use deno_core::FsModuleLoader; use deno_core::FsModuleLoader;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_fs::RealFs;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::worker::MainWorker; use deno_runtime::worker::MainWorker;
use deno_runtime::worker::WorkerOptions; use deno_runtime::worker::WorkerOptions;
@ -34,7 +37,9 @@ async fn main() -> Result<(), AnyError> {
eprintln!("Running {main_module}..."); eprintln!("Running {main_module}...");
let mut worker = MainWorker::bootstrap_from_options( let mut worker = MainWorker::bootstrap_from_options(
main_module.clone(), main_module.clone(),
PermissionsContainer::allow_all(), PermissionsContainer::allow_all(Arc::new(
RuntimePermissionDescriptorParser::new(Arc::new(RealFs)),
)),
WorkerOptions { WorkerOptions {
module_loader: Rc::new(FsModuleLoader), module_loader: Rc::new(FsModuleLoader),
extensions: vec![hello_runtime::init_ops_and_esm()], extensions: vec![hello_runtime::init_ops_and_esm()],

View file

@ -33,6 +33,7 @@ pub mod fs_util;
pub mod inspector_server; pub mod inspector_server;
pub mod js; pub mod js;
pub mod ops; pub mod ops;
pub mod permissions;
pub mod snapshot; pub mod snapshot;
pub mod tokio_util; pub mod tokio_util;
pub mod web_worker; pub mod web_worker;

View file

@ -123,10 +123,9 @@ fn op_fs_events_open(
RecursiveMode::NonRecursive RecursiveMode::NonRecursive
}; };
for path in &args.paths { for path in &args.paths {
let path = PathBuf::from(path); let path = state
state
.borrow_mut::<PermissionsContainer>() .borrow_mut::<PermissionsContainer>()
.check_read(&path, "Deno.watchFs()")?; .check_read(path, "Deno.watchFs()")?;
watcher.watch(&path, recursive_mode)?; watcher.watch(&path, recursive_mode)?;
} }
let resource = FsEventsResource { let resource = FsEventsResource {

View file

@ -1,7 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use ::deno_permissions::parse_sys_kind; use ::deno_permissions::parse_sys_kind;
use ::deno_permissions::NetDescriptor; use ::deno_permissions::PermissionDescriptorParser;
use ::deno_permissions::PermissionState; use ::deno_permissions::PermissionState;
use ::deno_permissions::PermissionsContainer; use ::deno_permissions::PermissionsContainer;
use deno_core::error::custom_error; use deno_core::error::custom_error;
@ -10,7 +10,7 @@ use deno_core::op2;
use deno_core::OpState; use deno_core::OpState;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use std::path::Path; use std::sync::Arc;
deno_core::extension!( deno_core::extension!(
deno_permissions, deno_permissions,
@ -19,6 +19,12 @@ deno_core::extension!(
op_revoke_permission, op_revoke_permission,
op_request_permission, op_request_permission,
], ],
options = {
permission_desc_parser: Arc<dyn PermissionDescriptorParser>,
},
state = |state, options| {
state.put(options.permission_desc_parser);
},
); );
#[derive(Deserialize)] #[derive(Deserialize)]
@ -56,15 +62,37 @@ pub fn op_query_permission(
state: &mut OpState, state: &mut OpState,
#[serde] args: PermissionArgs, #[serde] args: PermissionArgs,
) -> Result<PermissionStatus, AnyError> { ) -> 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 path = args.path.as_deref();
let perm = match args.name.as_ref() { let perm = match args.name.as_ref() {
"read" => permissions.read.query(path.map(Path::new)), "read" => permissions.read.query(
"write" => permissions.write.query(path.map(Path::new)), 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( "net" => permissions.net.query(
match args.host.as_deref() { match args.host.as_deref() {
None => None, None => None,
Some(h) => Some(NetDescriptor::parse(h)?), Some(h) => Some(desc_parser.parse_net_descriptor(h)?),
} }
.as_ref(), .as_ref(),
), ),
@ -72,8 +100,24 @@ pub fn op_query_permission(
"sys" => permissions "sys" => permissions
.sys .sys
.query(args.kind.as_deref().map(parse_sys_kind).transpose()?), .query(args.kind.as_deref().map(parse_sys_kind).transpose()?),
"run" => permissions.run.query(args.command.as_deref()), "run" => permissions.run.query(
"ffi" => permissions.ffi.query(args.path.as_deref().map(Path::new)), 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 => { n => {
return Err(custom_error( return Err(custom_error(
"ReferenceError", "ReferenceError",
@ -90,15 +134,37 @@ pub fn op_revoke_permission(
state: &mut OpState, state: &mut OpState,
#[serde] args: PermissionArgs, #[serde] args: PermissionArgs,
) -> Result<PermissionStatus, AnyError> { ) -> 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 path = args.path.as_deref();
let perm = match args.name.as_ref() { let perm = match args.name.as_ref() {
"read" => permissions.read.revoke(path.map(Path::new)), "read" => permissions.read.revoke(
"write" => permissions.write.revoke(path.map(Path::new)), 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( "net" => permissions.net.revoke(
match args.host.as_deref() { match args.host.as_deref() {
None => None, None => None,
Some(h) => Some(NetDescriptor::parse(h)?), Some(h) => Some(desc_parser.parse_net_descriptor(h)?),
} }
.as_ref(), .as_ref(),
), ),
@ -106,8 +172,24 @@ pub fn op_revoke_permission(
"sys" => permissions "sys" => permissions
.sys .sys
.revoke(args.kind.as_deref().map(parse_sys_kind).transpose()?), .revoke(args.kind.as_deref().map(parse_sys_kind).transpose()?),
"run" => permissions.run.revoke(args.command.as_deref()), "run" => permissions.run.revoke(
"ffi" => permissions.ffi.revoke(args.path.as_deref().map(Path::new)), 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 => { n => {
return Err(custom_error( return Err(custom_error(
"ReferenceError", "ReferenceError",
@ -124,15 +206,37 @@ pub fn op_request_permission(
state: &mut OpState, state: &mut OpState,
#[serde] args: PermissionArgs, #[serde] args: PermissionArgs,
) -> Result<PermissionStatus, AnyError> { ) -> 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 path = args.path.as_deref();
let perm = match args.name.as_ref() { let perm = match args.name.as_ref() {
"read" => permissions.read.request(path.map(Path::new)), "read" => permissions.read.request(
"write" => permissions.write.request(path.map(Path::new)), 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( "net" => permissions.net.request(
match args.host.as_deref() { match args.host.as_deref() {
None => None, None => None,
Some(h) => Some(NetDescriptor::parse(h)?), Some(h) => Some(desc_parser.parse_net_descriptor(h)?),
} }
.as_ref(), .as_ref(),
), ),
@ -140,8 +244,24 @@ pub fn op_request_permission(
"sys" => permissions "sys" => permissions
.sys .sys
.request(args.kind.as_deref().map(parse_sys_kind).transpose()?), .request(args.kind.as_deref().map(parse_sys_kind).transpose()?),
"run" => permissions.run.request(args.command.as_deref()), "run" => permissions.run.request(
"ffi" => permissions.ffi.request(args.path.as_deref().map(Path::new)), 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 => { n => {
return Err(custom_error( return Err(custom_error(
"ReferenceError", "ReferenceError",

View file

@ -17,12 +17,13 @@ use deno_io::ChildStderrResource;
use deno_io::ChildStdinResource; use deno_io::ChildStdinResource;
use deno_io::ChildStdoutResource; use deno_io::ChildStdoutResource;
use deno_permissions::PermissionsContainer; use deno_permissions::PermissionsContainer;
use deno_permissions::RunPathQuery; use deno_permissions::RunQueryDescriptor;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsString;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::ExitStatus; use std::process::ExitStatus;
@ -536,9 +537,9 @@ fn compute_run_cmd_and_check_permissions(
.with_context(|| format!("Failed to spawn '{}'", arg_cmd))?; .with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
check_run_permission( check_run_permission(
state, state,
RunPathQuery { &RunQueryDescriptor::Path {
requested: arg_cmd, requested: arg_cmd.to_string(),
resolved: &cmd, resolved: cmd.clone(),
}, },
&run_env, &run_env,
api_name, api_name,
@ -547,7 +548,7 @@ fn compute_run_cmd_and_check_permissions(
} }
struct RunEnv { struct RunEnv {
envs: HashMap<String, String>, envs: HashMap<OsString, OsString>,
cwd: PathBuf, cwd: PathBuf,
} }
@ -567,11 +568,32 @@ fn compute_run_env(
.map(|cwd_arg| resolve_path(cwd_arg, &cwd)) .map(|cwd_arg| resolve_path(cwd_arg, &cwd))
.unwrap_or(cwd); .unwrap_or(cwd);
let envs = if arg_clear_env { 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 { } 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 { 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 envs
}; };
@ -585,19 +607,7 @@ fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, AnyError> {
if is_path { if is_path {
Ok(resolve_path(cmd, &env.cwd)) Ok(resolve_path(cmd, &env.cwd))
} else { } else {
let path = env.envs.get("PATH").or_else(|| { let path = env.envs.get(&OsString::from("PATH"));
if cfg!(windows) {
env.envs.iter().find_map(|(k, v)| {
if k.to_uppercase() == "PATH" {
Some(v)
} else {
None
}
})
} else {
None
}
});
match which::which_in(cmd, path, &env.cwd) { match which::which_in(cmd, path, &env.cwd) {
Ok(cmd) => Ok(cmd), Ok(cmd) => Ok(cmd),
Err(which::Error::CannotFindBinaryPath) => { Err(which::Error::CannotFindBinaryPath) => {
@ -614,7 +624,7 @@ fn resolve_path(path: &str, cwd: &Path) -> PathBuf {
fn check_run_permission( fn check_run_permission(
state: &mut OpState, state: &mut OpState,
cmd: RunPathQuery, cmd: &RunQueryDescriptor,
run_env: &RunEnv, run_env: &RunEnv,
api_name: &str, api_name: &str,
) -> Result<(), AnyError> { ) -> 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_") 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 let mut found_envs = env
.envs .envs
.iter() .iter()
.filter(|(k, v)| requires_allow_all(k) && !v.trim().is_empty()) .filter_map(|(k, v)| {
.map(|(k, _)| k.as_str()) let key = k.to_str()?;
if requires_allow_all(key) && !is_empty(v) {
Some(key)
} else {
None
}
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
found_envs.sort(); found_envs.sort();
found_envs found_envs

View file

@ -19,6 +19,7 @@ use deno_core::ModuleSpecifier;
use deno_core::OpState; use deno_core::OpState;
use deno_permissions::create_child_permissions; use deno_permissions::create_child_permissions;
use deno_permissions::ChildPermissionsArg; use deno_permissions::ChildPermissionsArg;
use deno_permissions::PermissionDescriptorParser;
use deno_permissions::PermissionsContainer; use deno_permissions::PermissionsContainer;
use deno_web::deserialize_js_transferables; use deno_web::deserialize_js_transferables;
use deno_web::JsMessageData; use deno_web::JsMessageData;
@ -153,13 +154,19 @@ fn op_create_worker(
"Worker.deno.permissions", "Worker.deno.permissions",
); );
} }
let permission_desc_parser = state
.borrow::<Arc<dyn PermissionDescriptorParser>>()
.clone();
let parent_permissions = state.borrow_mut::<PermissionsContainer>(); let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = if let Some(child_permissions_arg) = args.permissions let worker_permissions = if let Some(child_permissions_arg) = args.permissions
{ {
let mut parent_permissions = parent_permissions.0.lock(); let mut parent_permissions = parent_permissions.inner.lock();
let perms = let perms = create_child_permissions(
create_child_permissions(&mut parent_permissions, child_permissions_arg)?; permission_desc_parser.as_ref(),
PermissionsContainer::new(perms) &mut parent_permissions,
child_permissions_arg,
)?;
PermissionsContainer::new(permission_desc_parser, perms)
} else { } else {
parent_permissions.clone() parent_permissions.clone()
}; };

164
runtime/permissions.rs Normal file
View file

@ -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());
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
use crate::ops; use crate::ops;
use crate::ops::bootstrap::SnapshotOptions; use crate::ops::bootstrap::SnapshotOptions;
use crate::permissions::RuntimePermissionDescriptorParser;
use crate::shared::maybe_transpile_source; use crate::shared::maybe_transpile_source;
use crate::shared::runtime; use crate::shared::runtime;
use deno_cache::SqliteBackedCache; use deno_cache::SqliteBackedCache;
@ -11,6 +12,7 @@ use deno_core::v8;
use deno_core::Extension; use deno_core::Extension;
use deno_http::DefaultHttpPropertyExtractor; use deno_http::DefaultHttpPropertyExtractor;
use deno_io::fs::FsError; use deno_io::fs::FsError;
use std::borrow::Cow;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
@ -45,29 +47,32 @@ impl deno_fetch::FetchPermissions for Permissions {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
fn check_read( fn check_read<'a>(
&mut self, &mut self,
_p: &Path, _p: &'a Path,
_api_name: &str, _api_name: &str,
) -> Result<(), deno_core::error::AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
} }
impl deno_ffi::FfiPermissions for Permissions { impl deno_ffi::FfiPermissions for Permissions {
fn check_partial( fn check_partial_no_path(
&mut self, &mut self,
_path: Option<&Path>,
) -> Result<(), deno_core::error::AnyError> { ) -> Result<(), deno_core::error::AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
fn check_partial_with_path(
&mut self,
_path: &str,
) -> Result<PathBuf, AnyError> {
unreachable!("snapshotting!")
}
} }
impl deno_napi::NapiPermissions for Permissions { impl deno_napi::NapiPermissions for Permissions {
fn check( fn check(&mut self, _path: &str) -> std::result::Result<PathBuf, AnyError> {
&mut self,
_path: Option<&Path>,
) -> Result<(), deno_core::error::AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
} }
@ -80,18 +85,24 @@ impl deno_node::NodePermissions for Permissions {
) -> Result<(), deno_core::error::AnyError> { ) -> Result<(), deno_core::error::AnyError> {
unreachable!("snapshotting!") 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( fn check_read_with_api_name(
&mut self, &mut self,
_p: &Path, _p: &str,
_api_name: Option<&str>, _api_name: Option<&str>,
) -> Result<(), deno_core::error::AnyError> { ) -> Result<PathBuf, deno_core::error::AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
fn check_write_with_api_name( fn check_write_with_api_name(
&mut self, &mut self,
_p: &Path, _p: &str,
_api_name: Option<&str>, _api_name: Option<&str>,
) -> Result<(), deno_core::error::AnyError> { ) -> Result<PathBuf, deno_core::error::AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
fn check_sys( fn check_sys(
@ -114,17 +125,25 @@ impl deno_net::NetPermissions for Permissions {
fn check_read( fn check_read(
&mut self, &mut self,
_p: &Path, _p: &str,
_api_name: &str, _api_name: &str,
) -> Result<(), deno_core::error::AnyError> { ) -> Result<PathBuf, deno_core::error::AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
fn check_write( fn check_write(
&mut self, &mut self,
_p: &Path, _p: &str,
_api_name: &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!") unreachable!("snapshotting!")
} }
} }
@ -143,9 +162,9 @@ impl deno_fs::FsPermissions for Permissions {
fn check_read( fn check_read(
&mut self, &mut self,
_path: &Path, _path: &str,
_api_name: &str, _api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
@ -164,17 +183,17 @@ impl deno_fs::FsPermissions for Permissions {
fn check_write( fn check_write(
&mut self, &mut self,
_path: &Path, _path: &str,
_api_name: &str, _api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
fn check_write_partial( fn check_write_partial(
&mut self, &mut self,
_path: &Path, _path: &str,
_api_name: &str, _api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
@ -190,22 +209,38 @@ impl deno_fs::FsPermissions for Permissions {
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
unreachable!("snapshotting!") 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 { impl deno_kv::sqlite::SqliteDbHandlerPermissions for Permissions {
fn check_read( fn check_read(
&mut self, &mut self,
_path: &Path, _path: &str,
_api_name: &str, _api_name: &str,
) -> Result<(), AnyError> { ) -> Result<PathBuf, AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
fn check_write( fn check_write<'a>(
&mut self, &mut self,
_path: &Path, _path: &'a Path,
_api_name: &str, _api_name: &str,
) -> Result<(), AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
unreachable!("snapshotting!") unreachable!("snapshotting!")
} }
} }
@ -255,7 +290,7 @@ pub fn create_runtime_snapshot(
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(), deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
deno_io::deno_io::init_ops_and_esm(Default::default()), deno_io::deno_io::init_ops_and_esm(Default::default()),
deno_fs::deno_fs::init_ops_and_esm::<Permissions>(fs.clone()), 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(), runtime::init_ops_and_esm(),
ops::runtime::deno_runtime::init_ops("deno:runtime".parse().unwrap()), ops::runtime::deno_runtime::init_ops("deno:runtime".parse().unwrap()),
ops::worker_host::deno_worker_host::init_ops( 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::fs_events::deno_fs_events::init_ops(),
ops::os::deno_os::init_ops(Default::default()), 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::process::deno_process::init_ops(),
ops::signal::deno_signal::init_ops(), ops::signal::deno_signal::init_ops(),
ops::tty::deno_tty::init_ops(), ops::tty::deno_tty::init_ops(),

View file

@ -44,6 +44,7 @@ use deno_http::DefaultHttpPropertyExtractor;
use deno_io::Stdio; use deno_io::Stdio;
use deno_kv::dynamic::MultiBackendDbHandler; use deno_kv::dynamic::MultiBackendDbHandler;
use deno_node::NodeExtInitServices; use deno_node::NodeExtInitServices;
use deno_permissions::PermissionDescriptorParser;
use deno_permissions::PermissionsContainer; use deno_permissions::PermissionsContainer;
use deno_terminal::colors; use deno_terminal::colors;
use deno_tls::RootCertStoreProvider; use deno_tls::RootCertStoreProvider;
@ -356,6 +357,7 @@ pub struct WebWorker {
} }
pub struct WebWorkerOptions { pub struct WebWorkerOptions {
// todo(dsherret): extract out the service structs from this options bag
pub bootstrap: BootstrapOptions, pub bootstrap: BootstrapOptions,
pub extensions: Vec<Extension>, pub extensions: Vec<Extension>,
pub startup_snapshot: Option<&'static [u8]>, pub startup_snapshot: Option<&'static [u8]>,
@ -377,6 +379,7 @@ pub struct WebWorkerOptions {
pub cache_storage_dir: Option<std::path::PathBuf>, pub cache_storage_dir: Option<std::path::PathBuf>,
pub stdio: Stdio, pub stdio: Stdio,
pub feature_checker: Arc<FeatureChecker>, pub feature_checker: Arc<FeatureChecker>,
pub permission_desc_parser: Arc<dyn PermissionDescriptorParser>,
pub strace_ops: Option<Vec<String>>, pub strace_ops: Option<Vec<String>>,
pub close_on_idle: bool, pub close_on_idle: bool,
pub maybe_worker_metadata: Option<WorkerMetadata>, pub maybe_worker_metadata: Option<WorkerMetadata>,
@ -501,7 +504,9 @@ impl WebWorker {
), ),
ops::fs_events::deno_fs_events::init_ops_and_esm(), ops::fs_events::deno_fs_events::init_ops_and_esm(),
ops::os::deno_os_worker::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::process::deno_process::init_ops_and_esm(),
ops::signal::deno_signal::init_ops_and_esm(), ops::signal::deno_signal::init_ops_and_esm(),
ops::tty::deno_tty::init_ops_and_esm(), ops::tty::deno_tty::init_ops_and_esm(),

View file

@ -49,6 +49,7 @@ use crate::code_cache::CodeCache;
use crate::code_cache::CodeCacheType; use crate::code_cache::CodeCacheType;
use crate::inspector_server::InspectorServer; use crate::inspector_server::InspectorServer;
use crate::ops; use crate::ops;
use crate::permissions::RuntimePermissionDescriptorParser;
use crate::shared::maybe_transpile_source; use crate::shared::maybe_transpile_source;
use crate::shared::runtime; use crate::shared::runtime;
use crate::BootstrapOptions; use crate::BootstrapOptions;
@ -157,6 +158,8 @@ pub struct WorkerOptions {
/// executed tries to load modules. /// executed tries to load modules.
pub module_loader: Rc<dyn ModuleLoader>, pub module_loader: Rc<dyn ModuleLoader>,
pub node_services: Option<NodeExtInitServices>, pub node_services: Option<NodeExtInitServices>,
pub permission_desc_parser:
Arc<dyn deno_permissions::PermissionDescriptorParser>,
// Callbacks invoked when creating new instance of WebWorker // Callbacks invoked when creating new instance of WebWorker
pub create_web_worker_cb: Arc<ops::worker_host::CreateWebWorkerCb>, pub create_web_worker_cb: Arc<ops::worker_host::CreateWebWorkerCb>,
pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>, pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>,
@ -201,13 +204,16 @@ pub struct WorkerOptions {
pub v8_code_cache: Option<Arc<dyn CodeCache>>, 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 { impl Default for WorkerOptions {
fn default() -> Self { fn default() -> Self {
let real_fs = Arc::new(deno_fs::RealFs);
Self { Self {
create_web_worker_cb: Arc::new(|_| { create_web_worker_cb: Arc::new(|_| {
unimplemented!("web workers are not supported") unimplemented!("web workers are not supported")
}), }),
fs: Arc::new(deno_fs::RealFs), fs: real_fs.clone(),
module_loader: Rc::new(FsModuleLoader), module_loader: Rc::new(FsModuleLoader),
skip_op_registration: false, skip_op_registration: false,
seed: None, seed: None,
@ -232,6 +238,9 @@ impl Default for WorkerOptions {
bootstrap: Default::default(), bootstrap: Default::default(),
stdio: Default::default(), stdio: Default::default(),
feature_checker: Default::default(), feature_checker: Default::default(),
permission_desc_parser: Arc::new(RuntimePermissionDescriptorParser::new(
real_fs,
)),
v8_code_cache: Default::default(), v8_code_cache: Default::default(),
} }
} }
@ -425,7 +434,9 @@ impl MainWorker {
), ),
ops::fs_events::deno_fs_events::init_ops_and_esm(), ops::fs_events::deno_fs_events::init_ops_and_esm(),
ops::os::deno_os::init_ops_and_esm(exit_code.clone()), 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::process::deno_process::init_ops_and_esm(),
ops::signal::deno_signal::init_ops_and_esm(), ops::signal::deno_signal::init_ops_and_esm(),
ops::tty::deno_tty::init_ops_and_esm(), ops::tty::deno_tty::init_ops_and_esm(),

View file

@ -255,12 +255,6 @@ itest!(_052_no_remote_flag {
http_server: true, 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 { itest!(_058_tasks_microtasks_close {
args: "run --quiet run/058_tasks_microtasks_close.ts", args: "run --quiet run/058_tasks_microtasks_close.ts",
output: "run/058_tasks_microtasks_close.ts.out", output: "run/058_tasks_microtasks_close.ts.out",

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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);
}

View file

@ -0,0 +1,4 @@
good [WILDCARD]subdir[WILDCARD]
good [WILDCARD]subdir[WILDCARD]
good [WILDCARD]subdir[WILDCARD]
good [WILDCARD]subdir[WILDCARD]

View file

@ -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);
}
}

View file

@ -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"
}
}
}

View file

@ -1,8 +1,9 @@
{ {
"args": "run --quiet -A main.ts", "tempDir": true,
"output": "main.out",
"envs": { "envs": {
"DYLD_FALLBACK_LIBRARY_PATH": "", "DYLD_FALLBACK_LIBRARY_PATH": "",
"LD_LIBRARY_PATH": "" "LD_LIBRARY_PATH": ""
} },
"args": "run --quiet -A main.ts",
"output": "main.out"
} }

View file

@ -3,7 +3,7 @@ PermissionStatus { state: "granted", onchange: null }
PermissionStatus { state: "prompt", onchange: null } PermissionStatus { state: "prompt", onchange: null }
PermissionStatus { state: "granted", 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 } PermissionStatus { state: "prompt", onchange: null }
PermissionStatus { state: "prompt", onchange: null } PermissionStatus { state: "prompt", onchange: null }

View file

@ -1,36 +1,41 @@
// Testing the following (but with `deno` instead of `echo`): // Testing the following:
// | `deno run --allow-run=echo` | `which path == "/usr/bin/echo"` at startup | `which path != "/usr/bin/echo"` at startup | // | `deno run --allow-run=binary` | `which path == "/usr/bin/binary"` at startup | `which path != "/usr/bin/binary"` at startup |
// |-------------------------------------|--------------------------------------------|--------------------------------------------| // |---------------------------------------|----------------------------------------------|--------------------------------------------|
// | **`Deno.Command("echo")`** | ✅ | ✅ | // | **`Deno.Command("binary")`** | :white_check_mark: | :white_check_mark: |
// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ❌ | // | **`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 | const binaryName = Deno.build.os === "windows" ? "binary.exe" : "binary";
// |-------------------------------------|--------------------------------------------|--------------------------------------------| const pathSep = Deno.build.os === "windows" ? "\\" : "/";
// | **`Deno.Command("echo")`** | ✅ | ❌ | const cwd = Deno.cwd();
// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ✅ | const execPathParent = `${Deno.cwd()}${pathSep}sub`;
const execPath = `${execPathParent}${pathSep}${binaryName}`;
const execPath = Deno.execPath(); Deno.mkdirSync(execPathParent);
const execPathParent = execPath.replace(/[/\\][^/\\]+$/, ""); Deno.copyFileSync(Deno.execPath(), execPath);
const testUrl = `data:application/typescript;base64,${ const testUrl = `data:application/typescript;base64,${
btoa(` 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: "${ console.error(await Deno.permissions.query({ name: "run", command: "${
execPath.replaceAll("\\", "\\\\") execPath.replaceAll("\\", "\\\\")
}" })); }" }));
Deno.env.set("PATH", ""); 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: "${ console.error(await Deno.permissions.query({ name: "run", command: "${
execPath.replaceAll("\\", "\\\\") execPath.replaceAll("\\", "\\\\")
}" })); }" }));
`) `)
}`; }`;
const process1 = await new Deno.Command(Deno.execPath(), { await new Deno.Command(Deno.execPath(), {
args: [ args: [
"run", "run",
"--allow-env", "--allow-env",
"--allow-run=deno", "--allow-run=binary",
testUrl, testUrl,
], ],
stdout: "inherit", stdout: "inherit",
@ -44,7 +49,7 @@ await new Deno.Command(Deno.execPath(), {
args: [ args: [
"run", "run",
"--allow-env", "--allow-env",
"--allow-run=deno", "--allow-run=binary",
testUrl, testUrl,
], ],
stderr: "inherit", stderr: "inherit",

View file

@ -6,11 +6,11 @@
}, },
"tests": { "tests": {
"env_arg": { "env_arg": {
"args": "run --allow-run=echo env_arg.ts", "args": "run --allow-run=curl env_arg.ts",
"output": "env_arg.out" "output": "env_arg.out"
}, },
"set_with_allow_env": { "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" "output": "set_with_allow_env.out"
} }
} }

View file

@ -1,7 +1,7 @@
Deno.env.set("LD_PRELOAD", "./libpreload.so"); Deno.env.set("LD_PRELOAD", "./libpreload.so");
try { try {
new Deno.Command("echo").spawn(); new Deno.Command("curl").spawn();
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
@ -9,7 +9,7 @@ try {
Deno.env.set("DYLD_FALLBACK_LIBRARY_PATH", "./libpreload.so"); Deno.env.set("DYLD_FALLBACK_LIBRARY_PATH", "./libpreload.so");
try { try {
Deno.run({ cmd: ["echo"] }).spawnSync(); Deno.run({ cmd: ["curl"] }).spawnSync();
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }

View file

@ -1 +0,0 @@
good [WILDCARD]subdir[WILDCARD]

View file

@ -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);
}

View file

@ -1,4 +1,16 @@
// deno-fmt-ignore-file // 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({ postMessage({
envGlobal: (await Deno.permissions.query({ name: "env" })).state, envGlobal: (await Deno.permissions.query({ name: "env" })).state,
envFoo: (await Deno.permissions.query({ name: "env", variable: "foo" })).state, envFoo: (await Deno.permissions.query({ name: "env", variable: "foo" })).state,
@ -15,11 +27,13 @@ postMessage({
readGlobal: (await Deno.permissions.query({ name: "read" })).state, readGlobal: (await Deno.permissions.query({ name: "read" })).state,
readFoo: (await Deno.permissions.query({ name: "read", path: new URL("foo", import.meta.url) })).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, 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, 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, runBar: (await Deno.permissions.query({ name: "run", command: "bar" })).state,
runBaz: (await Deno.permissions.query({ name: "run", command: "./baz" })).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, runAbsent: (await Deno.permissions.query({ name: "run", command: "absent" })).state,
writeGlobal: (await Deno.permissions.query({ name: "write" })).state, writeGlobal: (await Deno.permissions.query({ name: "write" })).state,
writeFoo: (await Deno.permissions.query({ name: "write", path: new URL("foo", import.meta.url) })).state, writeFoo: (await Deno.permissions.query({ name: "write", path: new URL("foo", import.meta.url) })).state,

View file

@ -79,7 +79,9 @@ Deno.test(
) => { ) => {
const src = ` const src = `
console.log( 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(), { const { success, stdout } = await new Deno.Command(Deno.execPath(), {
args: ["eval", src], args: ["eval", src],

View file

@ -5,6 +5,7 @@
// Requires to be run with `--allow-net` flag // Requires to be run with `--allow-net` flag
import { assert, assertEquals, assertMatch, assertThrows } from "@std/assert"; import { assert, assertEquals, assertMatch, assertThrows } from "@std/assert";
import { toFileUrl } from "@std/path/to-file-url";
function resolveWorker(worker: string): string { function resolveWorker(worker: string): string {
return import.meta.resolve(`../testdata/workers/${worker}`); return import.meta.resolve(`../testdata/workers/${worker}`);
@ -442,7 +443,31 @@ Deno.test("Worker limit children permissions", async function () {
worker.terminate(); 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 () { Deno.test("Worker limit children permissions granularly", async function () {
const ctx = setupReadCheckGranularWorkerTest();
const workerUrl = resolveWorker("read_check_granular_worker.js"); const workerUrl = resolveWorker("read_check_granular_worker.js");
const worker = new Worker( const worker = new Worker(
workerUrl, workerUrl,
@ -453,8 +478,13 @@ Deno.test("Worker limit children permissions granularly", async function () {
env: ["foo"], env: ["foo"],
net: ["foo", "bar:8000"], net: ["foo", "bar:8000"],
ffi: [new URL("foo", workerUrl), "bar"], ffi: [new URL("foo", workerUrl), "bar"],
read: [new URL("foo", workerUrl), "bar"], read: [new URL("foo", workerUrl), "bar", ctx.tempDir],
run: [new URL("foo", workerUrl), "bar", "./baz"], run: [
toFileUrl(ctx.runFooFilePath),
"bar",
"./baz",
"unresolved-exec",
],
write: [new URL("foo", workerUrl), "bar"], write: [new URL("foo", workerUrl), "bar"],
}, },
}, },
@ -482,8 +512,10 @@ Deno.test("Worker limit children permissions granularly", async function () {
readAbsent: "prompt", readAbsent: "prompt",
runGlobal: "prompt", runGlobal: "prompt",
runFoo: "granted", runFoo: "granted",
runFooPath: "granted",
runBar: "granted", runBar: "granted",
runBaz: "granted", runBaz: "granted",
runUnresolved: "prompt", // unresolved binaries remain as "prompt"
runAbsent: "prompt", runAbsent: "prompt",
writeGlobal: "prompt", writeGlobal: "prompt",
writeFoo: "granted", writeFoo: "granted",
@ -494,6 +526,7 @@ Deno.test("Worker limit children permissions granularly", async function () {
}); });
Deno.test("Nested worker limit children permissions", 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 */ /** This worker has permissions but doesn't grant them to its children */
const worker = new Worker( const worker = new Worker(
resolveWorker("parent_read_check_worker.js"), resolveWorker("parent_read_check_worker.js"),
@ -521,8 +554,10 @@ Deno.test("Nested worker limit children permissions", async function () {
readAbsent: "prompt", readAbsent: "prompt",
runGlobal: "prompt", runGlobal: "prompt",
runFoo: "prompt", runFoo: "prompt",
runFooPath: "prompt",
runBar: "prompt", runBar: "prompt",
runBaz: "prompt", runBaz: "prompt",
runUnresolved: "prompt",
runAbsent: "prompt", runAbsent: "prompt",
writeGlobal: "prompt", writeGlobal: "prompt",
writeFoo: "prompt", writeFoo: "prompt",

View file

@ -218,7 +218,7 @@ async function ensureNoNewITests() {
"pm_tests.rs": 0, "pm_tests.rs": 0,
"publish_tests.rs": 0, "publish_tests.rs": 0,
"repl_tests.rs": 0, "repl_tests.rs": 0,
"run_tests.rs": 334, "run_tests.rs": 333,
"shared_library_tests.rs": 0, "shared_library_tests.rs": 0,
"task_tests.rs": 4, "task_tests.rs": 4,
"test_tests.rs": 0, "test_tests.rs": 0,