1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-23 15:16:54 -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::url::Url;
use deno_graph::GraphKind;
use deno_runtime::colors;
use deno_runtime::deno_permissions::parse_sys_kind;
use deno_runtime::deno_permissions::PermissionsOptions;
use log::debug;
@ -661,107 +660,25 @@ impl PermissionFlags {
|| self.deny_write.is_some()
}
pub fn to_options(
&self,
// will be None when `deno compile` can't resolve the cwd
initial_cwd: Option<&Path>,
) -> Result<PermissionsOptions, AnyError> {
fn convert_option_str_to_path_buf(
flag: &Option<Vec<String>>,
initial_cwd: Option<&Path>,
) -> Result<Option<Vec<PathBuf>>, AnyError> {
let Some(paths) = &flag else {
return Ok(None);
};
let mut new_paths = Vec::with_capacity(paths.len());
for path in paths {
if let Some(initial_cwd) = initial_cwd {
new_paths.push(initial_cwd.join(path))
} else {
let path = PathBuf::from(path);
if path.is_absolute() {
new_paths.push(path);
} else {
bail!("Could not resolve relative permission path '{}' when current working directory could not be resolved.", path.display())
}
}
}
Ok(Some(new_paths))
}
fn resolve_allow_run(
allow_run: &[String],
) -> Result<Vec<PathBuf>, AnyError> {
let mut new_allow_run = Vec::with_capacity(allow_run.len());
for command_name in allow_run {
if command_name.is_empty() {
bail!("Empty command name not allowed in --allow-run=...")
}
let command_path_result = which::which(command_name);
match command_path_result {
Ok(command_path) => new_allow_run.push(command_path),
Err(err) => {
log::info!(
"{} Failed to resolve '{}' for allow-run: {}",
colors::gray("Info"),
command_name,
err
);
}
}
}
Ok(new_allow_run)
}
let mut deny_write =
convert_option_str_to_path_buf(&self.deny_write, initial_cwd)?;
let allow_run = self
.allow_run
.as_ref()
.and_then(|raw_allow_run| match resolve_allow_run(raw_allow_run) {
Ok(resolved_allow_run) => {
if resolved_allow_run.is_empty() && !raw_allow_run.is_empty() {
None // convert to no permissions if now empty
} else {
Some(Ok(resolved_allow_run))
}
}
Err(err) => Some(Err(err)),
})
.transpose()?;
// add the allow_run list to deno_write
if let Some(allow_run_vec) = &allow_run {
if !allow_run_vec.is_empty() {
let deno_write = deny_write.get_or_insert_with(Vec::new);
deno_write.extend(allow_run_vec.iter().cloned());
}
}
Ok(PermissionsOptions {
pub fn to_options(&self) -> PermissionsOptions {
PermissionsOptions {
allow_all: self.allow_all,
allow_env: self.allow_env.clone(),
deny_env: self.deny_env.clone(),
allow_net: self.allow_net.clone(),
deny_net: self.deny_net.clone(),
allow_ffi: convert_option_str_to_path_buf(&self.allow_ffi, initial_cwd)?,
deny_ffi: convert_option_str_to_path_buf(&self.deny_ffi, initial_cwd)?,
allow_read: convert_option_str_to_path_buf(
&self.allow_read,
initial_cwd,
)?,
deny_read: convert_option_str_to_path_buf(&self.deny_read, initial_cwd)?,
allow_run,
allow_ffi: self.allow_ffi.clone(),
deny_ffi: self.deny_ffi.clone(),
allow_read: self.allow_read.clone(),
deny_read: self.deny_read.clone(),
allow_run: self.allow_run.clone(),
deny_run: self.deny_run.clone(),
allow_sys: self.allow_sys.clone(),
deny_sys: self.deny_sys.clone(),
allow_write: convert_option_str_to_path_buf(
&self.allow_write,
initial_cwd,
)?,
deny_write,
allow_write: self.allow_write.clone(),
deny_write: self.deny_write.clone(),
prompt: !resolve_no_prompt(self),
})
}
}
}

View file

@ -3,7 +3,6 @@
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_runtime::deno_permissions::PermissionsContainer;
use crate::file_fetcher::FileFetcher;
@ -17,7 +16,7 @@ pub async fn resolve_import_map_value_from_specifier(
Ok(serde_json::from_str(&data_url_text)?)
} else {
let file = file_fetcher
.fetch(specifier, &PermissionsContainer::allow_all())
.fetch_bypass_permissions(specifier)
.await?
.into_text_decoded()?;
Ok(serde_json::from_str(&file.source)?)

View file

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

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -299,10 +299,15 @@ impl Drop for ResourceToBodyAdapter {
pub trait FetchPermissions {
fn check_net_url(
&mut self,
_url: &Url,
url: &Url,
api_name: &str,
) -> Result<(), AnyError>;
fn check_read(&mut self, _p: &Path, api_name: &str) -> Result<(), AnyError>;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_read<'a>(
&mut self,
p: &'a Path,
api_name: &str,
) -> Result<Cow<'a, Path>, AnyError>;
}
impl FetchPermissions for deno_permissions::PermissionsContainer {
@ -316,12 +321,16 @@ impl FetchPermissions for deno_permissions::PermissionsContainer {
}
#[inline(always)]
fn check_read(
fn check_read<'a>(
&mut self,
path: &Path,
path: &'a Path,
api_name: &str,
) -> Result<(), AnyError> {
deno_permissions::PermissionsContainer::check_read(self, path, api_name)
) -> Result<Cow<'a, Path>, AnyError> {
deno_permissions::PermissionsContainer::check_read_path(
self,
path,
Some(api_name),
)
}
}
@ -359,7 +368,11 @@ where
type_error("NetworkError when attempting to fetch resource")
})?;
let permissions = state.borrow_mut::<FP>();
permissions.check_read(&path, "fetch()")?;
let path = permissions.check_read(&path, "fetch()")?;
let url = match path {
Cow::Owned(path) => Url::from_file_path(path).unwrap(),
Cow::Borrowed(_) => url,
};
if method != Method::GET {
return Err(type_error(format!(

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ use deno_core::error::AnyError;
use std::mem::size_of;
use std::os::raw::c_char;
use std::os::raw::c_short;
use std::path::Path;
use std::path::PathBuf;
mod call;
mod callback;
@ -41,13 +41,28 @@ const _: () = {
pub const UNSTABLE_FEATURE_NAME: &str = "ffi";
pub trait FfiPermissions {
fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError>;
fn check_partial_no_path(&mut self) -> Result<(), AnyError>;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn check_partial_with_path(
&mut self,
path: &str,
) -> Result<PathBuf, AnyError>;
}
impl FfiPermissions for deno_permissions::PermissionsContainer {
#[inline(always)]
fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
deno_permissions::PermissionsContainer::check_ffi_partial(self, path)
fn check_partial_no_path(&mut self) -> Result<(), AnyError> {
deno_permissions::PermissionsContainer::check_ffi_partial_no_path(self)
}
#[inline(always)]
fn check_partial_with_path(
&mut self,
path: &str,
) -> Result<PathBuf, AnyError> {
deno_permissions::PermissionsContainer::check_ffi_partial_with_path(
self, path,
)
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

@ -255,12 +255,6 @@ itest!(_052_no_remote_flag {
http_server: true,
});
itest!(_056_make_temp_file_write_perm {
args:
"run --quiet --allow-read --allow-write=./subdir/ run/056_make_temp_file_write_perm.ts",
output: "run/056_make_temp_file_write_perm.out",
});
itest!(_058_tasks_microtasks_close {
args: "run --quiet run/058_tasks_microtasks_close.ts",
output: "run/058_tasks_microtasks_close.ts.out",

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",
"output": "main.out",
"tempDir": true,
"envs": {
"DYLD_FALLBACK_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: "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 }

View file

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

View file

@ -6,11 +6,11 @@
},
"tests": {
"env_arg": {
"args": "run --allow-run=echo env_arg.ts",
"args": "run --allow-run=curl env_arg.ts",
"output": "env_arg.out"
},
"set_with_allow_env": {
"args": "run --allow-run=echo --allow-env set_with_allow_env.ts",
"args": "run --allow-run=curl --allow-env set_with_allow_env.ts",
"output": "set_with_allow_env.out"
}
}

View file

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

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
import { toFileUrl } from "@std/path/to-file-url";
function tryGetCwd() {
// will throw in one test but not the other
try {
return Deno.cwd()
} catch {
return import.meta.dirname;
}
}
const fooExePath = tryGetCwd() + "/foo" + (Deno.build.os === "windows" ? ".exe" : "");
postMessage({
envGlobal: (await Deno.permissions.query({ name: "env" })).state,
envFoo: (await Deno.permissions.query({ name: "env", variable: "foo" })).state,
@ -15,11 +27,13 @@ postMessage({
readGlobal: (await Deno.permissions.query({ name: "read" })).state,
readFoo: (await Deno.permissions.query({ name: "read", path: new URL("foo", import.meta.url) })).state,
readBar: (await Deno.permissions.query({ name: "read", path: "bar" })).state,
readAbsent: (await Deno.permissions.query({ name: "read", path: "absent" })).state,
readAbsent: (await Deno.permissions.query({ name: "read", path: "../absent" })).state,
runGlobal: (await Deno.permissions.query({ name: "run" })).state,
runFoo: (await Deno.permissions.query({ name: "run", command: new URL("foo", import.meta.url) })).state,
runFoo: (await Deno.permissions.query({ name: "run", command: toFileUrl(fooExePath) })).state,
runFooPath: (await Deno.permissions.query({ name: "run", command: fooExePath })).state,
runBar: (await Deno.permissions.query({ name: "run", command: "bar" })).state,
runBaz: (await Deno.permissions.query({ name: "run", command: "./baz" })).state,
runUnresolved: (await Deno.permissions.query({ name: "run", command: "unresolved-exec" })).state,
runAbsent: (await Deno.permissions.query({ name: "run", command: "absent" })).state,
writeGlobal: (await Deno.permissions.query({ name: "write" })).state,
writeFoo: (await Deno.permissions.query({ name: "write", path: new URL("foo", import.meta.url) })).state,

View file

@ -79,7 +79,9 @@ Deno.test(
) => {
const src = `
console.log(
${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env.get(k))
${
JSON.stringify(Object.keys(expectedEnv))
}.map(k => Deno.env.get(k) ?? null)
)`;
const { success, stdout } = await new Deno.Command(Deno.execPath(), {
args: ["eval", src],

View file

@ -5,6 +5,7 @@
// Requires to be run with `--allow-net` flag
import { assert, assertEquals, assertMatch, assertThrows } from "@std/assert";
import { toFileUrl } from "@std/path/to-file-url";
function resolveWorker(worker: string): string {
return import.meta.resolve(`../testdata/workers/${worker}`);
@ -442,7 +443,31 @@ Deno.test("Worker limit children permissions", async function () {
worker.terminate();
});
function setupReadCheckGranularWorkerTest() {
const tempDir = Deno.realPathSync(Deno.makeTempDirSync());
const initialPath = Deno.env.get("PATH")!;
const initialCwd = Deno.cwd();
Deno.chdir(tempDir);
const envSep = Deno.build.os === "windows" ? ";" : ":";
Deno.env.set("PATH", initialPath + envSep + tempDir);
// create executables that will be resolved when doing `which`
const ext = Deno.build.os === "windows" ? ".exe" : "";
Deno.copyFileSync(Deno.execPath(), tempDir + "/bar" + ext);
return {
tempDir,
runFooFilePath: tempDir + "/foo" + ext,
[Symbol.dispose]() {
Deno.removeSync(tempDir, { recursive: true });
Deno.env.set("PATH", initialPath);
Deno.chdir(initialCwd);
},
};
}
Deno.test("Worker limit children permissions granularly", async function () {
const ctx = setupReadCheckGranularWorkerTest();
const workerUrl = resolveWorker("read_check_granular_worker.js");
const worker = new Worker(
workerUrl,
@ -453,8 +478,13 @@ Deno.test("Worker limit children permissions granularly", async function () {
env: ["foo"],
net: ["foo", "bar:8000"],
ffi: [new URL("foo", workerUrl), "bar"],
read: [new URL("foo", workerUrl), "bar"],
run: [new URL("foo", workerUrl), "bar", "./baz"],
read: [new URL("foo", workerUrl), "bar", ctx.tempDir],
run: [
toFileUrl(ctx.runFooFilePath),
"bar",
"./baz",
"unresolved-exec",
],
write: [new URL("foo", workerUrl), "bar"],
},
},
@ -482,8 +512,10 @@ Deno.test("Worker limit children permissions granularly", async function () {
readAbsent: "prompt",
runGlobal: "prompt",
runFoo: "granted",
runFooPath: "granted",
runBar: "granted",
runBaz: "granted",
runUnresolved: "prompt", // unresolved binaries remain as "prompt"
runAbsent: "prompt",
writeGlobal: "prompt",
writeFoo: "granted",
@ -494,6 +526,7 @@ Deno.test("Worker limit children permissions granularly", async function () {
});
Deno.test("Nested worker limit children permissions", async function () {
const _cleanup = setupReadCheckGranularWorkerTest();
/** This worker has permissions but doesn't grant them to its children */
const worker = new Worker(
resolveWorker("parent_read_check_worker.js"),
@ -521,8 +554,10 @@ Deno.test("Nested worker limit children permissions", async function () {
readAbsent: "prompt",
runGlobal: "prompt",
runFoo: "prompt",
runFooPath: "prompt",
runBar: "prompt",
runBaz: "prompt",
runUnresolved: "prompt",
runAbsent: "prompt",
writeGlobal: "prompt",
writeFoo: "prompt",

View file

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